27

I'm having a table with all translations in a "ext_translations" table.

The translating works great. The problem is now: I want to manage those translations via sonata-admin bundle.

I have already found a documentation, how to get work doctrine extensions with sonata admin. But in my case I have ONE table/entity for all my translations (for multiple entities).

So according to this documentation: http://www.elao.com/blog/symfony-2/doctrine-2/how-to-manage-translations-for-your-object-using-sonataadminbundle.html what should be my mappedBy attribute (see below)?

ext_translations table:

mysql> show columns from ext_translations;
+--------------+--------------+------+-----+---------+----------------+
| Field        | Type         | Null | Key | Default | Extra          |
+--------------+--------------+------+-----+---------+----------------+
| id           | int(11)      | NO   | PRI | NULL    | auto_increment |
| locale       | varchar(8)   | NO   | MUL | NULL    |                |
| object_class | varchar(255) | NO   |     | NULL    |                |
| field        | varchar(32)  | NO   |     | NULL    |                |
| foreign_key  | varchar(64)  | NO   |     | NULL    |                |
| content      | longtext     | YES  |     | NULL    |                |
+--------------+--------------+------+-----+---------+----------------+

MappedBy:

   /**
     * @ORM\OneToMany(targetEntity="ProfileTranslation", mappedBy="object", cascade={"persist", "remove"})
     */
    protected $translations;

As far as I understood the problem here: "I have a composite key (objectclass (the entity) + name (of the attribute) + foreignKey (id of entity)), so how should the 'mappedBy' refer to this?

I don't want to create an extra class for each translatable entity (like in the tutorial from above)

PeeHaa
  • 71,436
  • 58
  • 190
  • 262
eav
  • 2,123
  • 7
  • 25
  • 34
  • I am not familiar with the SonataAdminBundle, but in general Doctrine terms what is wrong with the "Use Case 1": http://docs.doctrine-project.org/en/latest/tutorials/composite-primary-keys.html In this case Doctrine will handle the relationships and return you the correct translations for the object? – Feras Nov 08 '13 at 17:50

1 Answers1

2

Your situation is quite complex. Probably the best could be if you dont use any anotation, override the repository class, and build all your own logic.

We can try to leverage the new ability since Doctrine 2.1 of creating composite Composite Keys as Primary Key, as Feras said in his comment.

Doctrine 2 supports composite primary keys natively. Composite keys are a very powerful relational database concept and we took good care to make sure Doctrine 2 supports as many of the composite primary key use-cases. For Doctrine 2.0 composite keys of primitive data-types are supported, for Doctrine 2.1 even foreign keys as primary keys are supported.

In the docs, we have a good example to a use case that is more or less similar to yours:

Dynamic Attributes of an Entity (for example Article). Each Article has many attributes with primary key “article_id” and “attribute_name”.

You can see the example here: http://docs.doctrine-project.org/en/latest/tutorials/composite-primary-keys.html#use-case-1-dynamic-attributes

But since that approach only considers one entity, we have to adapt it to your needs. We can follow this steps:

  1. Create a sort of views of your ext_translations table

    CREATE VIEW profile_ext_translations
    AS 
    SELECT * 
    FROM ext_translations
    WHERE  object_class = 'Profile'
    
  2. Then create different entities, for that views, so i.e. you would have an entity ProfileExtTranslations with a composite primary key, as follows:

    **
    * @Entity
    */
    class ProfileExtTranslations
    {
    
      /**
      * @ORM\ManyToOne(targetEntity="Profile", inversedBy="translations")   
      * @ORM\JoinColumn(name="foreign_key", referencedColumnName="id", onDelete="CASCADE")*/
       private $profile;
    
     /** @Id @Column(type="string") */
     private $field;
    
     //Other fields and methods
    
    
    }
    
  3. And now, the code of the Profile entity, in the mappedBy of the translations, you just use:

    /**
    * @ORM\OneToMany(targetEntity="ProfileExtTranslation", mappedBy="profile", cascade={"persist", "remove"})
    */
    protected $translations;
    

And with this and probably a litle tunning, you should have it working.

Carlos Robles
  • 10,828
  • 3
  • 41
  • 60
  • Looks like a good step by step solution. I did the steps 1 and 2, but I'm getting a bit confused in step 3 since I'm using GedmoTranslatable (https://github.com/l3pp4rd/DoctrineExtensions/blob/master/doc/translatable.md). I don't have a property like $translations, I've something like a property which I want to get translated (e.g. name) and then it's annotated with @Gedmo\Translatable. In my ext_translations the field called 'field' identifies this. So I have a row like this in my ext_translations: object_class='Profile', field='name'. – eav Feb 04 '14 at 07:28
  • yeah, i get that, but i used translations since is what you mentioned in your question, and also its what there is in the example, in the profile class. if you are following the tutorial you linked, you should have a translation property that somehow is filled by Gedmo or Doctrine. I dont know how Gedmo works, but like this, your scenario should be equivalent to the scenario in the example, so you will be able to figure it out – Carlos Robles Feb 04 '14 at 07:34
  • I'll again take a look at it today - and you will get your bounty ;-) – eav Feb 04 '14 at 07:55
  • Its on my list for this weekend, I'm sorry carlos for not answering. Can't I give you the bounty now? Am I too late? If yes, I would re-bount this question, so that you could earn the points =) – eav Feb 12 '14 at 07:05
  • Oh, no, but thanks! I dont want you to invest more reputation! But thank you. For next time just remember that after 7 days, the unasigned bounties just goes to nobody (you lose the points and bobody earn them). I hope the solution works or at least gives you some clue on how to solve it! – Carlos Robles Feb 12 '14 at 08:30
  • I know 100 are not that much, but you should definitely get it =) – eav Feb 12 '14 at 08:52