简体   繁体   中英

Managing one to many entities in sonata admin bundle


Imagine you are developing a website using symfony2 and it's admin panel using Sonata Admin Bundle and of course imagine you have a class named Product which owns some Images. Image is also a class and you have set in Product a one to many relation-ship to Image class. So every image is owned by a product, so you want to manage Image Admin inside Product Admin classes.
So there are some problems you should to face with them. Would you please to tell how?
1. When you delete a product object, all images related to that product be deleted.
2. When you are in show product page or add new product page the picture of all of images display on page.
(Is there any solution without using sonata media bundle too?)
Thanks

I handle Image upload with Document class:

 FstQst\\WebBundle\\Entity\\Document: type: entity table: null id: id: type: integer id: true generator: strategy: AUTO fields: updated: # changed when files are uploaded, to force preUpdate and postUpdate to fire type: datetime nullable: true format: type: string length: 25 nullable: true lifecycleCallbacks: prePersist: [ preUpload ] preUpdate: [ preUpload ] postPersist: [upload] postUpdate: [upload] postRemove: [removeUpload] 

I have another class using Document for adding some features to image:

 FstQst\\WebBundle\\Entity\\Post: type: entity table: null id: id: type: integer id: true generator: strategy: AUTO fields: title: type: string length: 100 nullable: true oneToOne: document: targetEntity: Document joinColumn: name: document_id referencedColumnName: id orphanRemoval: true manyToOne: site: targetEntity: VisitablePoint inversedBy: posts joinColumn: name: vPoint_id referencedColumnName: id lifecycleCallbacks: { } 

And a class named VisitablePoint, which uses Post class:

 FstQst\\WebBundle\\Entity\\VisitablePoint: type: entity table: visitablePoint id: id: type: integer id: true generator: strategy: AUTO oneToMany: posts: targetEntity: Post mappedBy: site orphanRemoval: true lifecycleCallbacks: { } 

I changed my classes names from Post to Image and from VisitablePoint to Product at my old post. Now I want when I go to VisitablePoint admin/show page I see pictures of Post object instead of their title. And of course in admin/edit page too.
And these are Admin Classes:

 <?php namespace FstQst\\WebBundle\\Admin; use FstQst\\WebBundle\\Entity\\Document; use Sonata\\AdminBundle\\Admin\\Admin; use Sonata\\AdminBundle\\Datagrid\\DatagridMapper; use Sonata\\AdminBundle\\Datagrid\\ListMapper; use Sonata\\AdminBundle\\Form\\FormMapper; use Sonata\\AdminBundle\\Show\\ShowMapper; class DocumentAdmin extends Admin { /** * @param ListMapper $listMapper */ protected function configureListFields(ListMapper $listMapper) { $listMapper ->add('id') ->add('updated') ->add('_action', 'actions', array( 'actions' => array( 'show' => array(), 'edit' => array(), 'delete' => array(), ) )) ; } /** * @param FormMapper $formMapper */ protected function configureFormFields(FormMapper $formMapper) { $formMapper ->add('file', 'file', $this->getFieldOptionForImagePreview()) ; } /** * @param ShowMapper $showMapper */ protected function configureShowFields(ShowMapper $showMapper) { $showMapper ->add('id') ->add('updated') ->add('format') ; } public function prePersist($image) { $this->manageFileUpload($image); } public function preUpdate($image) { $this->manageFileUpload($image); } protected function manageFileUpload(Document $image) { if ($image->getFile()) { $image->refreshUpdated(); } } protected function getFieldOptionForImagePreview($maxSize = 200){ if($this->hasParentFieldDescription()) { // this Admin is embedded // $getter will be something like 'getlogoImage' $getter = 'get' . $this->getParentFieldDescription()->getFieldName(); // get hold of the parent object $parent = $this->getParentFieldDescription()->getAdmin()->getSubject(); if ($parent) { $document = $parent->$getter(); } else { $document = null; } } else { $document = $this->getSubject(); } // use $fileFieldOptions so we can add other options to the field $fileFieldOptions = array('required' => false); if ($document && ($webPath = $document->getWebPath())) { // get the container so the full path to the image can be set $container = $this->getConfigurationPool()->getContainer(); $fullPath = $container->get('request')->getBasePath().'/'.$webPath; //$fileFieldOptions['help'] = '<img src="/uploads/documents/10.png" class="admin-preview" style="max-height: 200px; max-width: 200px"/>'; $fileFieldOptions['help'] = <<<START <img src="$fullPath" style="max-height: {$maxSize}px; max-width: {$maxSize}px"/> START; } return $fileFieldOptions; } } 

 <?php namespace FstQst\\WebBundle\\Admin; use Sonata\\AdminBundle\\Admin\\Admin; use Sonata\\AdminBundle\\Datagrid\\DatagridMapper; use Sonata\\AdminBundle\\Datagrid\\ListMapper; use Sonata\\AdminBundle\\Form\\FormMapper; use Sonata\\AdminBundle\\Show\\ShowMapper; class VisitablePointAdmin extends Admin { /** * @param FormMapper $formMapper */ protected function configureFormFields(FormMapper $formMapper) { $formMapper ->add('posts', 'sonata_type_model', array('multiple' => true, 'property' => 'title', 'label' => 'Image', 'required' => false)) ; } /** * @param ShowMapper $showMapper */ protected function configureShowFields(ShowMapper $showMapper) { $showMapper ->add('posts', 'sonata_type_collection', ['label' => 'Images', 'required' => false, 'cascade_validation' => true, 'by_reference' => false], ['edit' => 'inline', 'inline' => 'table']) ->end() ; } } 

The One to Many relation

1. When you delete a product object, all images related to that product be deleted.

You can easily manage this kind of deletion with the orphanRemoval="true"

<?php
class Product{
    [...]
    /**
     * @ORM\OneToMany(targetEntity="Event", mappedBy="day", orphanRemoval="true", cascade={"all"})
     */
    private $images;
    [...]
}

in yml, the configuration looks like:

oneToMany:
  images:
    targetEntity: Image
    orphanRemoval: true
    mappedBy: product

2. When you are in show product page or add new product page the picture of all of images display on page.

You have to use a sonata_type_collection in your configureFormFields or your configureShowFields method of your ProductAdmin class:

<?php
class ProductAdmin{
    [...]
    protected function configureFormFields(FormMapper $formMapper) {
        $formMapper
            ->with('tab_images')
                ->add('images', 'sonata_type_collection', array(
                    'required' => false,
                    'cascade_validation' => true,
                    'by_reference' => false,
                ), array(
                    'edit' => 'inline',
                    'inline' => 'table',
                ))
            ->end()
        ;
    }
    [...]
}

Then provide a ImageAdmin with all you need to upload files. You might have to change settings because i took it from a personal project and i don't know if it's totally adapted to your needs.

Show images in Sonata

first configure your ImageAdmin as follow:

class ImageAdmin extends Admin
{
    [...]
    protected function configureShowFields(ShowMapper $showMapper) {
        $showMapper
            ->add('myImageAttr', 'image', array(
                'prefix' => '/',
            ))
        ;
    }
    [...]
    protected function configureListFields(ListMapper $listMapper) {
        $listMapper
            ->add('myImageAttr', 'image', array(
                'prefix' => '/',
                'width' => 100
            ))
        ;
    }
}

then create template for your list_image and you show_image types:

Resources/views/CRUD/show_image.html.twig

{% extends 'SonataAdminBundle:CRUD:base_show_field.html.twig' %}
{% block field %}
    {% if value %}
        <img src="{% if field_description.options.prefix %}{{ field_description.options.prefix }}{% endif %}{{ value }}" />
    {% endif %}
{% endblock %}

Resources/views/CRUD/list_image.html.twig

{% extends admin.getTemplate('base_list_field') %}

{% block field%}
{% spaceless %}
    {% set width = field_description.options.width is defined ? field_description.options.width : 50 %}
    {% set height = field_description.options.height is defined ? field_description.options.height : 50 %}
    {% if value %}
        <img src="{% if field_description.options.prefix is defined %}{{ field_description.options.prefix }}{% endif %}{{ value }}" 
            style="max-width:{{ width }}px; max-height:{{ height }}px;" />
    {% else %}
        <div class="no-image" style="width:{{ width }}px; height:{{ height }}px;"></div>
    {% endif %}
{% endspaceless %}
{% endblock %}

finally, add this configuration into your app/config/config.yml

sonata_doctrine_orm_admin:
  templates:
    types:
      show:
        image: YourBundle:CRUD:show_image.html.twig
      list:
        image: YourBundle:CRUD:list_image.html.twig

( http://sonata-project.org/bundles/doctrine-orm-admin/master/doc/reference/templates.html )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM