2015-05-25 14 views
7

Học thuyết lệnh của Symfony: generate: crud tạo bộ điều khiển < biểu mẫu và chế độ xem của nó. Tuy nhiên, chỉ mục không chứa các trường tham chiếu bảng khác "nhiều-một". mô hìnhCrud Symfony tạo chế độ xem chỉ mục, trong đó không có trường tham chiếu

Entity:

<?php 

namespace Acme\Bundle\AdminBundle\Entity; 

use Doctrine\ORM\Mapping as ORM; 

/** 
* Albums 
* 
* @ORM\Table(name="albums", indexes={@ORM\Index(name="IDX_F4E2474F3D8E604F", columns={"parent"})}) 
* @ORM\Entity 
*/ 
class Albums 
{ 
    /** 
    * @var integer 
    * 
    * @ORM\Column(name="id", type="integer", nullable=false) 
    * @ORM\Id 
    * @ORM\GeneratedValue(strategy="SEQUENCE") 
    * @ORM\SequenceGenerator(sequenceName="albums_id_seq", allocationSize=1, initialValue=1) 
    */ 
    private $id; 

    /** 
    * @var string 
    * 
    * @ORM\Column(name="name", type="string", length=60, nullable=false) 
    */ 
    private $name; 

    /** 
    * @var integer 
    * 
    * @ORM\Column(name="sort", type="integer", nullable=false) 
    */ 
    private $sort; 

    /** 
    * @var \ParentAlbums 
    * 
    * @ORM\ManyToOne(targetEntity="ParentAlbums") 
    * @ORM\JoinColumns({ 
    * @ORM\JoinColumn(name="parent", referencedColumnName="id") 
    * }) 
    */ 
    private $parent; 



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

    /** 
    * Set name 
    * 
    * @param string $name 
    * @return Albums 
    */ 
    public function setName($name) 
    { 
     $this->name = $name; 

     return $this; 
    } 

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

    /** 
    * Set sort 
    * 
    * @param integer $sort 
    * @return Albums 
    */ 
    public function setSort($sort) 
    { 
     $this->sort = $sort; 

     return $this; 
    } 

    /** 
    * Get sort 
    * 
    * @return integer 
    */ 
    public function getSort() 
    { 
     return $this->sort; 
    } 

    /** 
    * Set parent 
    * 
    * @param \Acme\Bundle\AdminBundle\Entity\ParentAlbums $parent 
    * @return Albums 
    */ 
    public function setParent(\Acme\Bundle\AdminBundle\Entity\ParentAlbums $parent = null) 
    { 
     $this->parent = $parent; 

     return $this; 
    } 

    /** 
    * Get parent 
    * 
    * @return \Acme\Bundle\AdminBundle\Entity\ParentAlbums 
    */ 
    public function getParent() 
    { 
     return $this->parent; 
    } 
} 

Index.html.twig - phần bảng đầu:

<thead> 
     <tr> 
      <th>Id</th> 
      <th>Name</th> 
      <th>Sort</th> 
      <th>{{ 'views.index.actions'|trans({}, 'JordiLlonchCrudGeneratorBundle') }}</th> 
     </tr> 
    </thead> 
lệnh

enter image description here

+0

Phải có 3 trường: tên, sắp xếp, cấp độ gốc. Trường gốc không được tạo. –

+0

symfony crud generateor lệnh: php app/giao diện điều khiển: tạo: crud --entity = AdminBundle: Album --route-prefix = admin/album --with-write --format = chú thích --không tương tác - ghi đè –

Trả lời

7

Đây là hành vi bình thường của DoctrineCrudGenerator, bởi vì các máy phát điện chỉ sử dụng các mảng Doctrine\ORM\Mapping\ClassMetadataInfo::$fieldMappings để tạo bảng, nhưng các ParentAlbum-ManyToOne liên kết nằm trong mảng Doctrine\ORM\Mapping\ClassMetadataInfo::$associationMappings. Vì vậy, nó sẽ không bao giờ được công nhận trên thế hệ crud.

Để chứng minh một giải pháp khả thi tôi sử dụng thực sự Comment của ứng dụng symfony-demo:

<?php 

/* 
* This file is part of the Symfony package. 
* 
* (c) Fabien Potencier <[email protected]> 
* 
* For the full copyright and license information, please view the LICENSE 
* file that was distributed with this source code. 
*/ 

namespace AppBundle\Entity; 

use Doctrine\ORM\Mapping as ORM; 
use Symfony\Component\Validator\Constraints as Assert; 

/** 
* @ORM\Entity 
* 
* Defines the properties of the Comment entity to represent the blog comments. 
* See http://symfony.com/doc/current/book/doctrine.html#creating-an-entity-class 
* 
* Tip: if you have an existing database, you can generate these entity class automatically. 
* See http://symfony.com/doc/current/cookbook/doctrine/reverse_engineering.html 
* 
* @author Ryan Weaver <[email protected]> 
* @author Javier Eguiluz <[email protected]> 
*/ 
class Comment 
{ 
    /** 
    * @ORM\Id 
    * @ORM\GeneratedValue 
    * @ORM\Column(type="integer") 
    */ 
    private $id; 

    /** 
    * @ORM\ManyToOne(targetEntity="Post", inversedBy="comments") 
    * @ORM\JoinColumn(nullable=false) 
    */ 
    private $post; 

    /** 
    * @ORM\Column(type="text") 
    * @Assert\NotBlank(message="Please don't leave your comment blank!") 
    * @Assert\Length(
    *  min = "5", 
    *  minMessage = "Comment is too short ({{ limit }} characters minimum)", 
    *  max = "10000", 
    *  maxMessage = "Comment is too long ({{ limit }} characters maximum)" 
    *) 
    */ 
    private $content; 

    /** 
    * @ORM\Column(type="string") 
    * @Assert\Email() 
    */ 
    private $authorEmail; 

    /** 
    * @ORM\Column(type="datetime") 
    * @Assert\DateTime() 
    */ 
    private $publishedAt; 

    public function __construct() 
    { 
     $this->publishedAt = new \DateTime(); 
    } 

    /** 
    * @Assert\True(message = "The content of this comment is considered spam.") 
    */ 
    public function isLegitComment() 
    { 
     $containsInvalidCharacters = false !== strpos($this->content, '@'); 

     return !$containsInvalidCharacters; 
    } 

    public function getId() 
    { 
     return $this->id; 
    } 

    public function getContent() 
    { 
     return $this->content; 
    } 

    public function setContent($content) 
    { 
     $this->content = $content; 
    } 

    public function getAuthorEmail() 
    { 
     return $this->authorEmail; 
    } 

    public function setAuthorEmail($authorEmail) 
    { 
     $this->authorEmail = $authorEmail; 
    } 

    public function getPublishedAt() 
    { 
     return $this->publishedAt; 
    } 

    public function setPublishedAt($publishedAt) 
    { 
     $this->publishedAt = $publishedAt; 
    } 

    public function getPost() 
    { 
     return $this->post; 
    } 

    public function setPost(Post $post = null) 
    { 
     $this->post = $post; 
    } 
} 

Có cột "bình thường" như id, nội dung, AUTHOREMAIL và publishedAt và hiệp hội một ManyToOne đến bài viết thực thể. Đối với thực thể này, siêu dữ liệu sau được tạo:

Doctrine\ORM\Mapping\ClassMetadata {#437 
    +name: "AppBundle\Entity\Comment" 
    +namespace: "AppBundle\Entity" 
    +rootEntityName: "AppBundle\Entity\Comment" 
    +customGeneratorDefinition: null 
    +customRepositoryClassName: null 
    +isMappedSuperclass: false 
    +isEmbeddedClass: false 
    +parentClasses: [] 
    +subClasses: [] 
    +embeddedClasses: [] 
    +namedQueries: [] 
    +namedNativeQueries: [] 
    +sqlResultSetMappings: [] 
    +identifier: array:1 [ 
    0 => "id" 
    ] 
    +inheritanceType: 1 
    +generatorType: 4 
    +fieldMappings: array:4 [ 
    "id" => array:9 [ 
     "fieldName" => "id" 
     "type" => "integer" 
     "scale" => 0 
     "length" => null 
     "unique" => false 
     "nullable" => false 
     "precision" => 0 
     "id" => true 
     "columnName" => "id" 
    ] 
    "content" => array:8 [ 
     "fieldName" => "content" 
     "type" => "text" 
     "scale" => 0 
     "length" => null 
     "unique" => false 
     "nullable" => false 
     "precision" => 0 
     "columnName" => "content" 
    ] 
    "authorEmail" => array:8 [ 
     "fieldName" => "authorEmail" 
     "type" => "string" 
     "scale" => 0 
     "length" => null 
     "unique" => false 
     "nullable" => false 
     "precision" => 0 
     "columnName" => "authorEmail" 
    ] 
    "publishedAt" => array:8 [ 
     "fieldName" => "publishedAt" 
     "type" => "datetime" 
     "scale" => 0 
     "length" => null 
     "unique" => false 
     "nullable" => false 
     "precision" => 0 
     "columnName" => "publishedAt" 
    ] 
    ] 
    +fieldNames: array:4 [ 
    "id" => "id" 
    "content" => "content" 
    "authorEmail" => "authorEmail" 
    "publishedAt" => "publishedAt" 
    ] 
    +columnNames: array:4 [ 
    "id" => "id" 
    "content" => "content" 
    "authorEmail" => "authorEmail" 
    "publishedAt" => "publishedAt" 
    ] 
    +discriminatorValue: null 
    +discriminatorMap: [] 
    +discriminatorColumn: null 
    +table: array:1 [ 
    "name" => "Comment" 
    ] 
    +lifecycleCallbacks: [] 
    +entityListeners: [] 
    +associationMappings: array:1 [ 
    "post" => array:19 [ 
     "fieldName" => "post" 
     "joinColumns" => array:1 [ 
     0 => array:6 [ 
      "name" => "post_id" 
      "unique" => false 
      "nullable" => false 
      "onDelete" => null 
      "columnDefinition" => null 
      "referencedColumnName" => "id" 
     ] 
     ] 
     "cascade" => [] 
     "inversedBy" => "comments" 
     "targetEntity" => "AppBundle\Entity\Post" 
     "fetch" => 2 
     "type" => 2 
     "mappedBy" => null 
     "isOwningSide" => true 
     "sourceEntity" => "AppBundle\Entity\Comment" 
     "isCascadeRemove" => false 
     "isCascadePersist" => false 
     "isCascadeRefresh" => false 
     "isCascadeMerge" => false 
     "isCascadeDetach" => false 
     "sourceToTargetKeyColumns" => array:1 [ 
     "post_id" => "id" 
     ] 
     "joinColumnFieldNames" => array:1 [ 
     "post_id" => "post_id" 
     ] 
     "targetToSourceKeyColumns" => array:1 [ 
     "id" => "post_id" 
     ] 
     "orphanRemoval" => false 
    ] 
    ] 
    +isIdentifierComposite: false 
    +containsForeignIdentifier: false 
    +idGenerator: Doctrine\ORM\Id\IdentityGenerator {#439 
    -sequenceName: null 
    } 
    +sequenceGeneratorDefinition: null 
    +tableGeneratorDefinition: null 
    +changeTrackingPolicy: 1 
    +isVersioned: null 
    +versionField: null 
    +cache: null 
    +reflClass: null 
    +isReadOnly: false 
    #namingStrategy: Doctrine\ORM\Mapping\DefaultNamingStrategy {#407} 
    +reflFields: array:5 [ 
    "id" => null 
    "content" => null 
    "authorEmail" => null 
    "publishedAt" => null 
    "post" => null 
    ] 
    -instantiator: Doctrine\Instantiator\Instantiator {#438} 
} 

Bạn có thể thấy, các trường thông thường nằm trong mảngMục điều khiển, trong khi liên kết nằm trong mảng liên kếtMẫu. Chạy máy phát điện crud cho đơn vị Comment sản xuất bàn duy nhất cho các cột "bình thường" mà không có bài liên quan:

<thead> 
    <tr> 
     <th>Id</th> 
     <th>Content</th> 
     <th>Authoremail</th> 
     <th>Publishedat</th> 
     <th>Actions</th> 
    </tr> 
</thead> 

Bây giờ, để thay đổi hành vi này, bạn chỉ cần phải "hợp nhất" mảng associationMappings trong fieldMappings mảng trên thế hệ crud. Bạn có thể làm điều đó trong số Sensio\Bundle\GeneratorBundle\Generator\DoctrineCrudGenerator. Để sản xuất, bạn phải ghi đè lên Sensio\Bundle\GeneratorBundle\Generator\DoctrineCrudGeneratorSensio\Bundle\GeneratorBundle\Command\GenerateDoctrineCrudCommand và thực hiện thay đổi trong lớp học của riêng bạn. Nhưng cũng giống như một bằng chứng của khái niệm tôi sẽ làm nhanh chóng và thực sự bẩn hack, một thêm thay đổi sau vào DoctrineCrudGenerator trực tiếp:

/** 
* Generates a CRUD controller. 
* 
* @author Fabien Potencier <[email protected]> 
*/ 
class DoctrineCrudGenerator extends Generator 
{ 

    // ... 

    /** 
    * Generates the index.html.twig template in the final bundle. 
    * 
    * @param string $dir The path to the folder that hosts templates in the bundle 
    */ 
    protected function generateIndexView($dir) 
    { 
     $this->renderFile(
      'crud/views/index.html.twig.twig', 
      $dir . '/index.html.twig', 
      array(
       'bundle' => $this->bundle->getName(), 
       'entity' => $this->entity, 
       'identifier' => $this->metadata->identifier[0], 

       // Use the function instead of the "raw" fieldMappings array 
       // 'fields' => $this->metadata->fieldMappings, 
       'fields' => $this->processFieldMappings(), 

       'actions' => $this->actions, 
       'record_actions' => $this->getRecordActions(), 
       'route_prefix' => $this->routePrefix, 
       'route_name_prefix' => $this->routeNamePrefix, 
      ) 
     ); 
    } 

    // ... 
    /** 
    * Add the associations to the array 
    * 
    * @return array 
    */ 
    protected function processFieldMappings() 
    { 

     /** @var \Doctrine\ORM\Mapping\ClassMetadata $metadata */ 
     $metadata = $this->metadata; 

     $fields = $metadata->fieldMappings; 
     $associationMappings = $metadata->associationMappings; 

     foreach ($associationMappings as $k => $a) { 
      // Add the field only if it is a ToOne association and if the targetEntity implements the __toString method 
      if ($a['type'] & ClassMetadataInfo::TO_ONE && method_exists($a['targetEntity'], '__toString')) { 
       $fields[$k] = array(
        "fieldName" => $a["fieldName"], 
        "type" => "text", 
        "scale" => 0, 
        "length" => null, 
        "unique" => false, 
        "nullable" => false, 
        "precision" => 0, 
        "columnName" => $k 
       ); 
      } 
     } 

     return $fields; 
    } 
} 

Sau những thay đổi và nếu bạn thêm __toString để đơn vị Bưu chính, máy phát điện tạo mã sau:

<table class="records_list"> 
    <thead> 
     <tr> 
      <th>Id</th> 
      <th>Content</th> 
      <th>Authoremail</th> 
      <th>Publishedat</th> 
      <th>Post</th> 
      <th>Actions</th> 
     </tr> 
    </thead> 
    <tbody> 
    {% for entity in entities %} 
     <tr> 
      <td><a href="{{ path('comment_show', { 'id': entity.id }) }}">{{ entity.id }}</a></td> 
      <td>{{ entity.content }}</td> 
      <td>{{ entity.authorEmail }}</td> 
      <td>{% if entity.publishedAt %}{{ entity.publishedAt|date('Y-m-d H:i:s') }}{% endif %}</td> 
      <td>{{ entity.post }}</td> 
      <td> 
      <ul> 
       <li> 
        <a href="{{ path('comment_show', { 'id': entity.id }) }}">show</a> 
       </li> 
       <li> 
        <a href="{{ path('comment_edit', { 'id': entity.id }) }}">edit</a> 
       </li> 
      </ul> 
      </td> 
     </tr> 
    {% endfor %} 
    </tbody> 
</table> 

Bạn có thể thấy, liên kết bài đăng được nhận dạng ngay bây giờ. Bạn có thể sử dụng điểm này làm điểm nhập nếu bạn muốn bắt đầu viết trình tạo của riêng bạn. Nhưng bạn phải điều tra, cách xử lý các hiệp hội ToMany và cách xử lý các liên kết ở các dạng và v.v ..

+0

Thực ra, tôi đã giải quyết vấn đề bằng một phương thức 'fields' => array_merge ($ this-> metadata-> fieldMappings, $ this-> metadata-> associationMappings), –

+0

Chắc chắn, bạn có thể. Nhưng nếu bạn muốn sử dụng 'array_merge' cho các liên kết ToMany, mà không cần tùy chỉnh các mẫu, bạn sẽ gặp sự cố. Tôi cũng không tuyên bố rằng phương pháp tiếp cận của tôi là phương pháp duy nhất. Điêu đo phụ thuộc vao bạn ;) – skroczek

-1

Các crud giúp bạn với một thế hệ nhanh chóng của nhiều file nhưng nó không phải mọi thứ. $ parent là một con trỏ tới một thực thể khác. Phương pháp crud không thể biết bạn muốn hiển thị những gì từ thực thể này. Hãy tưởng tượng rằng ParentAlbums có thuộc tính '$ name'. bạn có thể hiển thị nó rất đơn giản bằng cách thay đổi một chút index.html.twig:

<thead> 
    <tr> 
     <th>Id</th> 
     <th>Name</th> 
     <th>Parent</th> 
     <th>Sort</th> 
     <th>{{ 'views.index.actions'|trans({}, 'JordiLlonchCrudGeneratorBundle') }}</th> 
    </tr> 
</thead> 
    <tbody> 
    {% for entity in entities %} 
     <tr> 
      <td>{{ entity.id }}</td> 
      <td>{{ entity.name }}</td> 
      <td>{{ entity.parent.name }}</td> 
      <td>{{ entity.sort }}</td> 
      <td> 
      <ul> 
        // actions here 
      </ul> 
      </td> 
     </tr> 
    {% endfor %} 
    </tbody> 
+0

Không cần viết entity.parent.name, entity.parent đủ đơn giản. –

Các vấn đề liên quan