Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite Translatable/Translation to use Interfaces instead of Traits #387

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 28 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,13 +187,13 @@ You now have a working `Category` that behaves like:
<a name="translatable" id="translatable"></a>
### translatable:

If you're working on a `Category` entity, the `Translatable` behavior expects a **CategoryTranslation** entity in the
If you're working on a `Category` entity, the `Translatable` behavior expects a **CategoryTranslation** entity in the
same folder of Category entity by default.

The default naming convention (or its customization via trait methods) avoids you to manually handle entity associations.
It is handled automatically by the TranslationSubscriber.

In order to use the Translatable trait, you will have to create this `CategoryTranslation` entity.
In order to use the Translatable trait and interface, you will have to create this `CategoryTranslation` entity.

``` php
<?php
Expand All @@ -204,7 +204,7 @@ use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* @ORM\Entity
*/
class CategoryTranslation
class CategoryTranslation implements ORMBehaviors\Translatable\TranslationInterface
{
use ORMBehaviors\Translatable\Translation;

Expand Down Expand Up @@ -253,8 +253,8 @@ class CategoryTranslation
}
}
```
The corresponding Category entity needs to `use ORMBehaviors\Translatable\Translatable;`
and should only contain fields that you do not need to translate.
The corresponding Category entity needs to implement `ORMBehaviors\Translatable\TranslatableInterface`
and `use ORMBehaviors\Translatable\Translatable;` and should only contain fields that you do not need to translate.

``` php
<?php
Expand All @@ -265,7 +265,7 @@ use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* @ORM\Entity
*/
class Category
class Category implements ORMBehaviors\Translatable\TranslatableInterface
{
use ORMBehaviors\Translatable\Translatable;

Expand All @@ -277,7 +277,7 @@ class Category
```


After updating the database, ie. with `./console doctrine:schema:update --force`,
After updating the database, ie. with `./console doctrine:schema:update --force`,
you can now work on translations using `translate` or `getTranslations` methods.

``` php
Expand All @@ -295,17 +295,19 @@ you can now work on translations using `translate` or `getTranslations` methods.

```



#### Override

In case you prefer to use a different class name for the translation entity,
In case you prefer to use a different class name for the translation entity,
or want to use a separate namespace, you have 2 ways :

If you want to define a custom translation entity class name globally :
Override the trait `Translatable` and his method `getTranslationEntityClass`
and the trait `Translation` and his method `getTranslatableEntityClass` in the translation entity.
Override the trait `Translatable` and his method `getTranslationEntityClass`
and the trait `Translation` and his method `getTranslatableEntityClass` in the translation entity.
If you override one, you also need to override the other to return the inverse class.

Example: Let's say you want to create a sub namespace AppBundle\Entity\Translation to stock translations classes
Example: Let's say you want to create a sub namespace AppBundle\Entity\Translation to stock translations classes
then put overrided traits in that folder.

``` php
Expand Down Expand Up @@ -355,19 +357,29 @@ trait TranslationTrait
}
```

If you use that way make sure you override trait parameters of DoctrineBehaviors :
If you use that way make sure you add your new trait to the list of traits triggering the mapping:

``` yaml
parameters:
knp.doctrine_behaviors.translatable_subscriber.translatable_trait: AppBundle\Entity\Translation\TranslatableTrait
knp.doctrine_behaviors.translatable_subscriber.translation_trait: AppBundle\Entity\Translation\TranslationTrait
knp.doctrine_behaviors.translatable_subscriber.translatable_traits: [ AppBundle\Entity\Translation\TranslatableTrait, Knp\DoctrineBehaviors\Model\Translatable\Translatable, Knp\DoctrineBehaviors\Model\Translatable\TranslatableProperties ]
knp.doctrine_behaviors.translatable_subscriber.translation_traits: [ AppBundle\Entity\Translation\TranslationTrait, Knp\DoctrineBehaviors\Model\Translatable\Translation, Knp\DoctrineBehaviors\Model\Translatable\TranslationProperties ]
```

If you want to define a custom translation entity class name just for a single translatable class :
Override the trait method `getTranslationEntityClass` in the translatable entity and `getTranslatableEntityClass`
in the translation entity. If you override one, you also need to override the other to return the inverse class.


If you need a deeper customization, you can add as many traits as you need to the trait list. Any class using one of
these traits (directly) will trigger the mapping of the fields defined in
`Knp\DoctrineBehaviors\Model\Translatable\TranslatableProperties`. So make sure to always either use it in your own
traits or provide equivalent properties.

You can also handle the mapping yourself. As long as you implements the interfaces
`Knp\DoctrineBehaviors\Model\Translatable\TranslatableInterface` and
`Knp\DoctrineBehaviors\Model\Translatable\TranslationInterface` for the appropriate class, the subscriber will handle
the translations properly.

#### guess the current locale

You can configure the way the subscriber guesses the current locale, by giving a callable as its first argument.
Expand All @@ -388,7 +400,7 @@ so that when you try to call `getName` (for example) it will return you the tran
{
return $this->proxyCurrentLocaleTranslation($method, $arguments);
}

// or do it with PropertyAccessor that ships with Symfony SE
// if your methods don't take any required arguments
public function __call($method, $arguments)
Expand Down Expand Up @@ -478,7 +490,7 @@ parameters:
```

`datetimetz` here is a useful one to use if you are working with a Postgres database, otherwise you may encounter some
timezone issues. For more information on this see:
timezone issues. For more information on this see:
<a href="http://doctrine-dbal.readthedocs.org/en/latest/reference/known-vendor-issues.html#datetime-datetimetz-and-time-types">http://doctrine-dbal.readthedocs.org/en/latest/reference/known-vendor-issues.html#datetime-datetimetz-and-time-types</a>

The default type is `datetime`.
Expand Down
5 changes: 0 additions & 5 deletions config/orm-services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ parameters:
knp.doctrine_behaviors.translatable_subscriber.class: Knp\DoctrineBehaviors\ORM\Translatable\TranslatableSubscriber
knp.doctrine_behaviors.translatable_subscriber.current_locale_callable.class: Knp\DoctrineBehaviors\ORM\Translatable\CurrentLocaleCallable
knp.doctrine_behaviors.translatable_subscriber.default_locale_callable.class: Knp\DoctrineBehaviors\ORM\Translatable\DefaultLocaleCallable
knp.doctrine_behaviors.translatable_subscriber.translatable_trait: Knp\DoctrineBehaviors\Model\Translatable\Translatable
knp.doctrine_behaviors.translatable_subscriber.translation_trait: Knp\DoctrineBehaviors\Model\Translatable\Translation
knp.doctrine_behaviors.translatable_subscriber.translatable_fetch_method: LAZY
knp.doctrine_behaviors.translatable_subscriber.translation_fetch_method: LAZY
knp.doctrine_behaviors.softdeletable_subscriber.class: Knp\DoctrineBehaviors\ORM\SoftDeletable\SoftDeletableSubscriber
Expand Down Expand Up @@ -40,8 +38,6 @@ services:
- "@knp.doctrine_behaviors.reflection.class_analyzer"
- "@knp.doctrine_behaviors.translatable_subscriber.current_locale_callable"
- "@knp.doctrine_behaviors.translatable_subscriber.default_locale_callable"
- "%knp.doctrine_behaviors.translatable_subscriber.translatable_trait%"
- "%knp.doctrine_behaviors.translatable_subscriber.translation_trait%"
- "%knp.doctrine_behaviors.translatable_subscriber.translatable_fetch_method%"
- "%knp.doctrine_behaviors.translatable_subscriber.translation_fetch_method%"
tags:
Expand Down Expand Up @@ -154,4 +150,3 @@ services:
- "%knp.doctrine_behaviors.sluggable_subscriber.sluggable_trait%"
tags:
- { name: doctrine.event_subscriber }

95 changes: 95 additions & 0 deletions src/Model/Translatable/TranslatableInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

/*
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Knp\DoctrineBehaviors\Model\Translatable;

/**
* Translatable interface.
*
* Should be used to tag entities, that needs to be translated.
*/
interface TranslatableInterface
{
/**
* Returns collection of translations.
*
* @return ArrayCollection
*/
public function getTranslations();

/**
* Returns collection of new translations.
*
* @return ArrayCollection
*/
public function getNewTranslations();

/**
* Adds new translation.
*
* @param TranslationInterface $translation The translation
*
* @return $this
*/
public function addTranslation(TranslationInterface $translation);

/**
* Removes specific translation.
*
* @param TranslationInterface $translation The translation
*/
public function removeTranslation(TranslationInterface $translation);

/**
* Returns translation for specific locale (creates new one if doesn't exists).
* If requested translation doesn't exist, it will first try to fallback default locale
* If any translation doesn't exist, it will be added to newTranslations collection.
* In order to persist new translations, call mergeNewTranslations method, before flush
*
* @param string $locale The locale (en, ru, fr) | null If null, will try with current locale
* @param bool $fallbackToDefault Whether fallback to default locale
*
* @return TranslationInterface
*/
public function translate($locale = null, $fallbackToDefault = true);

/**
* Merges newly created translations into persisted translations.
*/
public function mergeNewTranslations();

/**
* @param mixed $locale the current locale
*/
public function setCurrentLocale($locale);

/**
* @return mixed Returns the current locale
*/
public function getCurrentLocale();

/**
* @param mixed $locale the default locale
*/
public function setDefaultLocale($locale);

/**
* @return mixed Returns the default locale
*/
public function getDefaultLocale();

/**
* Returns translation entity class name.
*
* @return string
*/
public static function getTranslationEntityClass();
}
18 changes: 9 additions & 9 deletions src/Model/Translatable/TranslatableMethods.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ public function getNewTranslations()
/**
* Adds new translation.
*
* @param Translation $translation The translation
* @param TranslationInterface $translation The translation
*
* @return $this
*/
public function addTranslation($translation)
public function addTranslation(TranslationInterface $translation)
{
$this->getTranslations()->set((string)$translation->getLocale(), $translation);
$translation->setTranslatable($this);
Expand All @@ -58,9 +58,9 @@ public function addTranslation($translation)
/**
* Removes specific translation.
*
* @param Translation $translation The translation
* @param TranslationInterface $translation The translation
*/
public function removeTranslation($translation)
public function removeTranslation(TranslationInterface $translation)
{
$this->getTranslations()->removeElement($translation);
}
Expand All @@ -74,7 +74,7 @@ public function removeTranslation($translation)
* @param string $locale The locale (en, ru, fr) | null If null, will try with current locale
* @param bool $fallbackToDefault Whether fallback to default locale
*
* @return Translation
* @return TranslationInterface
*/
public function translate($locale = null, $fallbackToDefault = true)
{
Expand All @@ -90,7 +90,7 @@ public function translate($locale = null, $fallbackToDefault = true)
* @param string $locale The locale (en, ru, fr) | null If null, will try with current locale
* @param bool $fallbackToDefault Whether fallback to default locale
*
* @return Translation
* @return TranslationInterface
*/
protected function doTranslate($locale = null, $fallbackToDefault = true)
{
Expand Down Expand Up @@ -171,10 +171,10 @@ public function getDefaultLocale()

/**
* An extra feature allows you to proxy translated fields of a translatable entity.
*
*
* @param string $method
* @param array $arguments
*
*
* @return mixed The translated value of the field for current locale
*/
protected function proxyCurrentLocaleTranslation($method, array $arguments = [])
Expand All @@ -201,7 +201,7 @@ public static function getTranslationEntityClass()
* @param string $locale The locale (en, ru, fr)
* @param bool $withNewTranslations searched in new translations too
*
* @return Translation|null
* @return TranslationInterface|null
*/
protected function findTranslationByLocale($locale, $withNewTranslations = true)
{
Expand Down
73 changes: 73 additions & 0 deletions src/Model/Translatable/TranslationInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

/*
* This file is part of the KnpDoctrineBehaviors package.
*
* (c) KnpLabs <http://knplabs.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Knp\DoctrineBehaviors\Model\Translatable;

/**
* Translation interface.
*
* Should be used to tag translation entities.
*/
interface TranslationInterface
{
/**
* Returns the translatable entity class name.
*
* @return string
*/
public static function getTranslatableEntityClass();

/**
* Returns object id.
*
* @return mixed
*/
public function getId();

/**
* Sets entity, that this translation should be mapped to.
*
* @param TranslatableInterface $translatable The translatable
*
* @return $this
*/
public function setTranslatable(TranslatableInterface $translatable);

/**
* Returns entity, that this translation is mapped to.
*
* @return TranslatableInterface
*/
public function getTranslatable();

/**
* Sets locale name for this translation.
*
* @param string $locale The locale
*
* @return $this
*/
public function setLocale($locale);

/**
* Returns this translation locale.
*
* @return string
*/
public function getLocale();

/**
* Tells if translation is empty
*
* @return bool true if translation is not filled
*/
public function isEmpty();
}
Loading