2017-08-11 186 views
2

有人在Symfony 3上翻譯了Sonata Admin實體(實際上我正在使用3.3)。Symfony 3上的Sonata管理實體翻譯

我嘗試了不同的解決方案,但都沒有真正奏效。 使用gedmo翻譯,主要問題是翻譯保存爲數據庫中的不同語言,但是在管理員(也是列表結束表單)中,Sonata軟件包僅顯示缺省語言環境翻譯,儘管不同的標誌/翻譯是點擊/選用。

我也試過用KNP tarnslation包和A2lix翻譯,但是這兩個有相同的問題:當你設置(在管理員類中)一個字段爲「可排序」,那麼當你試圖在記錄列表中按照字段排序,Symfony拋出一個錯誤,因爲翻譯系統試圖克隆與另一個領域不存在的關聯!

無論如何,留在Gedmo的靈魂,主要問題是(因爲我已經提到的問題,拆分A2lix解決方案)我不知道如何設置字段在管理類中可翻譯(BlogPostAdmin.php ),因爲簡單地使用配置文件和實體和翻譯類,似乎沒有工作。正如已經說過的那樣,問題在於翻譯保存在數據庫中,但不會顯示在管理列表/表單中。

這裏是我的配置和實體文件:

AppKernel.php

<?php 

use Symfony\Component\HttpKernel\Kernel; 
use Symfony\Component\Config\Loader\LoaderInterface; 

class AppKernel extends Kernel 
{ 
    public function registerBundles() 
    { 
     $bundles = [ 
      new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), 
      new Symfony\Bundle\SecurityBundle\SecurityBundle(), 
      new Symfony\Bundle\TwigBundle\TwigBundle(), 
      new Symfony\Bundle\MonologBundle\MonologBundle(), 
      new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(), 
      new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(), 
      new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), 
      new AppBundle\AppBundle(), 
      /// These are the other bundles the SonataAdminBundle relies on 
      new Sonata\CoreBundle\SonataCoreBundle(), 
      new Sonata\BlockBundle\SonataBlockBundle(), 
      new Knp\Bundle\MenuBundle\KnpMenuBundle(), 
      new Sonata\TranslationBundle\SonataTranslationBundle(), 

      // And finally, the storage and SonataAdminBundle 
      new Sonata\DoctrineORMAdminBundle\SonataDoctrineORMAdminBundle(), 
      new Sonata\AdminBundle\SonataAdminBundle(), 

      // stof [used in Sonata translations] 
      new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(), 

      // assetic 
      new Symfony\Bundle\AsseticBundle\AsseticBundle(), 
     ]; 

     if (in_array($this->getEnvironment(), ['dev', 'test'], true)) { 
      $bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle(); 
      $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle(); 
      $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle(); 

      if ('dev' === $this->getEnvironment()) { 
       $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle(); 
       $bundles[] = new Symfony\Bundle\WebServerBundle\WebServerBundle(); 
      } 
     } 

     return $bundles; 
    } 

    public function getRootDir() 
    { 
     return __DIR__; 
    } 

    public function getCacheDir() 
    { 
     return dirname(__DIR__).'/var/cache/'.$this->getEnvironment(); 
    } 

    public function getLogDir() 
    { 
     return dirname(__DIR__).'/var/logs'; 
    } 

    public function registerContainerConfiguration(LoaderInterface $loader) 
    { 
     $loader->load($this->getRootDir().'/config/config_'.$this->getEnvironment().'.yml'); 
    } 
} 

config.yml

imports: 
    - { resource: parameters.yml } 
    - { resource: security.yml } 
    - { resource: services.yml } 

# Put parameters here that don't need to change on each machine where the app is deployed 
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration 
parameters: 
    locale: it 

framework: 
    #esi: ~ 
    translator: { fallbacks: ['%locale%'] } 
    secret: '%secret%' 
    router: 
     resource: '%kernel.project_dir%/app/config/routing.yml' 
     strict_requirements: ~ 
    form: ~ 
    csrf_protection: ~ 
    validation: { enable_annotations: true } 
    #serializer: { enable_annotations: true } 
    templating: 
     engines: ['twig'] 
    default_locale: '%locale%' 
    trusted_hosts: ~ 
    session: 
     # https://symfony.com/doc/current/reference/configuration/framework.html#handler-id 
     handler_id: session.handler.native_file 
     save_path: '%kernel.project_dir%/var/sessions/%kernel.environment%' 
    fragments: ~ 
    http_method_override: true 
    assets: ~ 
    php_errors: 
     log: true 

# Twig Configuration 
twig: 
    debug: '%kernel.debug%' 
    strict_variables: '%kernel.debug%' 

# Doctrine Configuration 
doctrine: 
    dbal: 
     driver: pdo_mysql 
     host: '%database_host%' 
     port: '%database_port%' 
     dbname: '%database_name%' 
     user: '%database_user%' 
     password: '%database_password%' 
     charset: UTF8 
     # if using pdo_sqlite as your database driver: 
     # 1. add the path in parameters.yml 
     #  e.g. database_path: "%kernel.project_dir%/var/data/data.sqlite" 
     # 2. Uncomment database_path in parameters.yml.dist 
     # 3. Uncomment next line: 
     #path: '%database_path%' 

    orm: 
     auto_generate_proxy_classes: '%kernel.debug%' 
     naming_strategy: doctrine.orm.naming_strategy.underscore 
     auto_mapping: true 
#  mappings: 
#   # Doctrine extensions 
#   translatable: 
#    type: annotation 
#    alias: Gedmo 
#    prefix: Gedmo\Translatable\Entity 
#    dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/MappedSuperclass" 

# Swiftmailer Configuration 
swiftmailer: 
    transport: '%mailer_transport%' 
    host: '%mailer_host%' 
    username: '%mailer_user%' 
    password: '%mailer_password%' 
    spool: { type: memory } 

sonata_block: 
    default_contexts: [cms] 
    blocks: 
     # enable the SonataAdminBundle block 
     sonata.admin.block.admin_list: 
      contexts: [admin] 

sonata_translation: 
    locales: [it, en] 
    default_locale: %locale% 
    # here enable the types you need 
    gedmo: 
     enabled: true 
# knplabs: 
#  enabled: true 
    #phpcr: 
    # enabled: true 

sonata_admin: 
    templates: 
     layout: admin/layout.html.twig 

assetic: 
    debug:   '%kernel.debug%' 
    use_controller: '%kernel.debug%' 
    filters: 
     cssrewrite: ~ 

#stof_doctrine_extensions: 
# #default_locale: %locale% 
# orm: 
#  default: 
#   sluggable: true 
#   timestampable: true 

services.yml

# Learn more about services, parameters and containers at 
# https://symfony.com/doc/current/service_container.html 
parameters: 
    locale: 'it' 
    locales: ['it', 'en'] 

services: 
    # default configuration for services in *this* file 
    _defaults: 
     # automatically injects dependencies in your services 
     autowire: true 
     # automatically registers your services as commands, event subscribers, etc. 
     autoconfigure: true 
     # this means you cannot fetch services directly from the container via $container->get() 
     # if you need to do this, you can override this setting on individual services 
     public: false 

    # makes classes in src/AppBundle available to be used as services 
    # this creates a service per class whose id is the fully-qualified class name 
    AppBundle\: 
     resource: '../../src/AppBundle/*' 
     # you can exclude directories or files 
     # but if a service is unused, it's removed anyway 
     exclude: '../../src/AppBundle/{Entity,Repository,Tests}' 

    # controllers are imported separately to make sure they're public 
    # and have a tag that allows actions to type-hint services 
    AppBundle\Controller\: 
     resource: '../../src/AppBundle/Controller' 
     public: true 
     tags: ['controller.service_arguments'] 

    # add more services, or override services that need manual wiring 
    # AppBundle\Service\ExampleService: 
    #  arguments: 
    #   $someArgument: 'some_value' 

    admin.category: 
      class: AppBundle\Admin\CategoryAdmin 
      arguments: [~, AppBundle\Entity\Category, ~] 
      tags: 
       - { name: sonata.admin, manager_type: orm, label: Category } 
      public: true 

    admin.blog_post: 
     class: AppBundle\Admin\BlogPostAdmin 
     arguments: [~, AppBundle\Entity\BlogPost, ~] 
     tags: 
      - { name: sonata.admin, manager_type: orm, label: Blog post } 
     public: true 


    # Doctrine Extension listeners to handle behaviors 
    gedmo.listener.translatable: 
     class: Gedmo\Translatable\TranslatableListener 
     tags: 
      - { name: doctrine.event_subscriber, connection: default } 
     calls: 
      #- [ setAnnotationReader, [ @annotation_reader ] ] 
      - [ setDefaultLocale, [ it ] ] 
      - [ setTranslationFallback, [ false ] ] 
      - [ setPersistDefaultLocaleTranslation, [ false ] ] 

BlogPost.php

<?php 

namespace AppBundle\Entity; 

use Doctrine\ORM\Mapping as ORM; 
use Sonata\TranslationBundle\Model\Gedmo\AbstractPersonalTranslatable; 
use Gedmo\Mapping\Annotation as Gedmo; 
use Sonata\TranslationBundle\Model\Gedmo\TranslatableInterface; 
use Doctrine\Common\Collections\ArrayCollection; 
use Sonata\TranslationBundle\Model\Gedmo\AbstractPersonalTranslation; 
use Sonata\TranslationBundle\Traits\Gedmo\PersonalTranslatableTrait; 

/** 
* BlogPost 
* 
* @ORM\Table(name="blog_post") 
* @ORM\Entity(repositoryClass="AppBundle\Repository\BlogPostRepository") 
* @Gedmo\TranslationEntity(class="AppBundle\Entity\Translations\BlogPostTr") 
* @ORM\HasLifecycleCallbacks 
*/ 

class BlogPost implements TranslatableInterface 
{ 
    use PersonalTranslatableTrait; 

    /** 
    * Post locale 
    * Used locale to override Translation listener's locale 
    * 
    * @Gedmo\Locale 
    */ 
    protected $locale; 

    /** 
    * @ORM\ManyToOne(targetEntity="Category", inversedBy="blogPosts") 
    */ 
    private $category; 

    public function setCategory(Category $category) 
    { 
     $this->category = $category; 
    } 

    public function getCategory() 
    { 
     return $this->category; 
    } 

    /** 
    * @var int 
    * 
    * @ORM\Column(name="id", type="integer") 
    * @ORM\Id 
    * @ORM\GeneratedValue(strategy="AUTO") 
    */ 
    private $id; 

    /** 
    * @var string 
    * 
    * @ORM\Column(name="title", type="string", length=255) 
    * @Gedmo\Translatable 
    */ 
    private $title; 

    /** 
    * @var string 
    * 
    * @ORM\Column(name="body", type="text") 
    * @Gedmo\Translatable 
    */ 
    private $body; 

    /** 
    * @var bool 
    * 
    * @ORM\Column(name="draft", type="boolean") 
    */ 
    private $draft = false; 


    /** 
    * Get id 
    * 
    * @return int 
    */ 
    public function getId() 
    { 
     return $this->id; 
    } 

    /** 
    * Set title 
    * 
    * @param string $title 
    * 
    * @return BlogPost 
    */ 
    public function setTitle($title) 
    { 
     $this->title = $title; 

     return $this; 
    } 

    /** 
    * Get title 
    * 
    * @return string 
    */ 
    public function getTitle() 
    { 
     return $this->title; 
    } 

    /** 
    * Set body 
    * 
    * @param string $body 
    * 
    * @return BlogPost 
    */ 
    public function setBody($body) 
    { 
     $this->body = $body; 

     return $this; 
    } 

    /** 
    * Get body 
    * 
    * @return string 
    */ 
    public function getBody() 
    { 
     return $this->body; 
    } 

    /** 
    * Set draft 
    * 
    * @param boolean $draft 
    * 
    * @return BlogPost 
    */ 
    public function setDraft($draft) 
    { 
     $this->draft = $draft; 

     return $this; 
    } 

    /** 
    * Get draft 
    * 
    * @return bool 
    */ 
    public function getDraft() 
    { 
     return $this->draft; 
    } 

    // TRANSLATION 
    /** 
    * @ORM\OneToMany(targetEntity="AppBundle\Entity\Translations\BlogPostTr", mappedBy="object", cascade={"persist", "remove"}) 
    */ 
    protected $translations; 


    public function __construct() 
    { 
     $this->translations = new ArrayCollection; 
    } 

    public function getTranslations() 
    { 
     return $this->translations; 
    } 

    public function addTranslation(AbstractPersonalTranslation $t) 
    { 
     $this->translations->add($t); 
     $t->setObject($this); 
    } 

    public function removeTranslation(AbstractPersonalTranslation $t) 
    { 
     $this->translations->removeElement($t); 
    } 

    public function setTranslations($translations) 
    { 
     $this->translations = $translations; 
    } 

    /** 
    * Sets translatable locale 
    * 
    * @param string $locale 
    */ 
    public function setTranslatableLocale($locale) 
    { 
     $this->locale = $locale; 
    } 
} 

BlogPostTr.php

<?php 
namespace AppBundle\Entity\Translations; 

use Doctrine\ORM\Mapping as ORM; 
use Sonata\TranslationBundle\Model\Gedmo\AbstractPersonalTranslation; 

/** 
* @ORM\Entity 
* @ORM\Table(name="blog_post_translation", 
*  uniqueConstraints={@ORM\UniqueConstraint(name="lookup_unique_idx", columns={ 
*   "locale", "object_id", "field" 
*  })} 
*) 
*/ 
class BlogPostTr extends AbstractPersonalTranslation 
{ 
    /** 
    * Convinient constructor 
    * 
    * @param string $locale 
    * @param string $field 
    * @param string $content 
    */ 
    public function __construct($locale = null, $field = null, $content = null) 
    { 
     $this->setLocale($locale); 
     $this->setField($field); 
     $this->setContent($content); 
    } 

    /** 
    * @ORM\ManyToOne(targetEntity="AppBundle\Entity\BlogPost", inversedBy="translations") 
    * @ORM\JoinColumn(name="object_id", referencedColumnName="id", onDelete="CASCADE") 
    */ 
    protected $object; 
} 

BlogPostAdmin.php

<?php 

namespace AppBundle\Admin; 

use Sonata\AdminBundle\Admin\AbstractAdmin; 
use Sonata\AdminBundle\Datagrid\ListMapper; 
use Sonata\AdminBundle\Form\FormMapper; 

class BlogPostAdmin extends AbstractAdmin 
{ 
    protected function configureFormFields(FormMapper $formMapper) 
    { 
     $formMapper 
      ->tab('Post') 
      ->with('Content', array('class' => 'col-md-9')) 
      ->add('title', 'text') 
//   ->add('title', 'translatable_field', array(
//    'allow_extra_fields' => true, 
//    'field' => 'title', 
//    'personal_translation' => 'AppBundle\Entity\Translations\BlogPostTr', 
//    'property_path' => 'translations', 
//   )) 
      ->add('body', 'textarea') 
      ->end() 
      ->end() 
      ->tab('Publishing options') 
      ->with('Meta data', array('class' => 'col-md-3')) 
      ->add('category', 'sonata_type_model', array(
       'class' => 'AppBundle\Entity\Category', 
       'property' => 'name', 
      )) 
      ->end() 
      ->end(); 
    } 





// protected function configureDatagridFilters(DatagridMapper $datagridMapper) 
// { 
//  $datagridMapper->add('title'); 
// } 

    protected function configureListFields(ListMapper $listMapper) 
    { 
     $listMapper->addIdentifier('title'); 
    } 

    public function toString($object) 
    { 
     return $object instanceof BlogPost 
      ? $object->getTitle() 
      : 'Blog Post'; // shown in the breadcrumb on the create view 
    } 
} 

請幫幫忙!

回答

1

有同樣的問題。其實我有兩個問題:

  1. 實體沒有實現Sonata \ TranslationBundle \ Model \ Gedmo \ TranslatableInterface。即使稍後添加,symfony緩存也需要清除。

  2. 我發現,在奏鳴曲管理員安裝或奏鳴曲包之一中,我添加了DoctrineExtensionListener,如shown here。聽衆裏面Gedmo啓動當前的語言環境,當然它不知道有關SonataTranslateBundle的任何信息。

因此,我建議轉儲從請求參數,奏鳴曲翻譯的語言環境(調試)symfony的語言環境和gedmo發起的區域設置和檢查,這些都是同步的。

最後,我設置了語言環境爲stored in user session,並更新了DoctrineExtensionListener以使用會話中的語言環境。