[SonataAdminBundle, SonataMediaBundle] ManyToOne and ManyToMany relations to Media

6,926 views
Skip to first unread message

Thomas Luzat

unread,
Nov 1, 2012, 12:39:51 PM11/1/12
to sonata...@googlegroups.com
Hello,

I have created a Doctrine entity News which contains two relations to SonataMediaBundle Media entities: A many-to-one relation ($media), which should refer to 0..1 media elements, and a many-to-many relation ($downloads), which should refer to 0..n media elements. Unfortunately I cannot get the NewsAdmin class to work (current code below). Currently, the problems are:

  1. media: This element is displayed like: Medienelement  (edit link with name of $media if $media is set) (list view button) (add button) (remove button)
    This would be fine if one could select existing media elements, too. But while the list view button opens a list view of all media in a JavaScript popup dialog, it does not seem to offer a way to choose any element. I tried switching to 'sonata_type_model', but this gave me just an ugly drop down box with no filtering capabilities to search for media (this is a MUST) and no way to edit the entities (this is not strictly required, but it would be a pity if it wasn't possible). If the list view offered a way to select a single Media element I would be happy.
  2. downloads: This element is displayed as a multi-select box with all media and an "add" button. There are two problems:
    • Usability: Is there something more usable apart from check boxes and multi-selects to select a collection of downloads? E.g. by using the list view? How?
    • Context: It seems that media for all contexts are displayed, while only media for the 'downloads' context should be visible. What's wrong?
I have tried different variations, but the code below shows the best that I could achieve so far. Ideas would be appreciated very much.

Cheers

Thomas Luzat


Versions: Symfony 2.1.2, SonataAdminBundle and SonataMediaBundle master, Doctrine 2.3, PHP 5.4

Code snippets:

// NewsAdmin:
$formMapper
    ->add('media', 'sonata_type_model_list', [
             'label' => 'Medienelement',
             'required' => false,
        ], ['link_parameters' => ['context' => 'news']])
    ->add('downloads', 'sonata_type_model', [
            'required'=> false,
            'multiple' => true,
            'by_reference' => false,
        ], ['link_parameters' => ['context' => 'downloads']])
// ...

/** @ORM\Entity */
class News {
    /**
     * @ORM\ManyToOne(targetEntity="\Application\Sonata\MediaBundle\Entity\Media")
     * @ORM\JoinColumn(nullable=true, onDelete="SET NULL")
     */
    protected $media;

    /**
     * @ORM\ManyToMany(targetEntity="\Application\Sonata\MediaBundle\Entity\Media", cascade={"all"})
     * @ORM\JoinTable(name="news_downloads",
     *      joinColumns={@ORM\JoinColumn(name="news_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="media_id", referencedColumnName="id", unique=true, onDelete="CASCADE")})
     */
    protected $downloads;
    // ...
}


Gabriel Richards

unread,
Jan 12, 2013, 3:23:05 AM1/12/13
to sonata...@googlegroups.com
Hello Thomas.

Did you ever find a solution?

I tried an approach just like yours for my many to many relationship, in my case I have "Unions" and "Roles".

When creating a Union, I sometimes want to also create Roles and associate them to the Union I'm creating (many to many).

When using sonata_type_collection as my form type, after setting all the options and attributes as you have, the form fails validation and then 3 multi-selects for the "Roles" field appear!

When NOT using a "sonata_type_*" the multi select works... but then I have to navigate to a different screen to first add the "Roles", or I'm going to have to implement my own "Add New" method... which seems awfully anti-DRY.

Gabe 

Stefano Alletti

unread,
Jan 24, 2013, 6:37:42 AM1/24/13
to sonata...@googlegroups.com
Hi Thomas, 

I have similar problem, have you founded a solution? 

In my case, i have a Page Entity with ManyToMany relationship with Media Entity. When i use "sonata_type_model", i have a multi-select box with all media and an "add" button, but i would like change displayed mode to a list of images with preview thumbnails. Any Idea? 

Thanks 

Stefano

Stefano Alletti

unread,
Jan 24, 2013, 9:09:15 AM1/24/13
to sonata...@googlegroups.com

Gabriel Richards

unread,
Jan 24, 2013, 3:43:15 PM1/24/13
to sonata...@googlegroups.com
Hello Stefano.

The solution I used for this was found by studying SonataMediaBundle's Admin & Entity / Model code more closely.

Basically, for this type of scenario, instead of creating a direct many to many relationship between your entities, you create two one to many relationships to a *linking entity* from either side... then your Admin class can use "sonata_type_collection" on the linking table to get the functionality you desire.

Here's some example code from my use case where I was dealing with Unions and Positions and the relationships between them.

First, the entities:

**
 * Union
 *
 * @ORM\Table(name="tcs_union")
 * @ORM\Entity
 */
class Union
{

    /**
     * @var Collection
     *
     * @ORM\OneToMany(targetEntity="TCS\TimecardBundle\Entity\PositionUnion", mappedBy="union", cascade={"persist"})
     */
    private $positionUnions;
}

**
 * Position
 *
 * @ORM\Table(name="tcs_position")
 * @ORM\Entity
 */
class Position
{

    /**
     * @var Collection
     *
     * @ORM\OneToMany(targetEntity="TCS\TimecardBundle\Entity\PositionUnion", mappedBy="position", cascade={"persist"})
     *
     */
    private $positionUnions;
}

/**
 * PositionUnion
 *
 * @ORM\Table(name="tcs_position_union")
 * @ORM\Entity
 */
class PositionUnion
{

    /**
     * @var Position
     *
     * @ORM\ManyToOne(targetEntity="TCS\TimecardBundle\Entity\Position", inversedBy="positionUnions", cascade={"persist"})
     */
    private $position;

    /**
     * @var Union
     *
     * @ORM\ManyToOne(targetEntity="TCS\TimecardBundle\Entity\Union", inversedBy="positionUnions", cascade={"persist"})
     */
    private $union;
}


Now the Admin Classes:

class Position extends BaseAdmin
{
    protected function configureFormFields(FormMapper $formMapper)
    {

        $formMapper
            ->add('name')
            ->with('Unions')
                ->add('positionUnions', 'sonata_type_collection', array('label' => false, 'required' => false), array(
                    'edit' => 'inline',
                    'inline' => 'table',
                ))
            ->end()
        ;
    }
}

class PositionUnion extends BaseAdmin
{
    protected function configureFormFields(FormMapper $formMapper)
    {

        $formMapper
            ->add('union', 'sonata_type_model', array('required' => false)
            )
        ;
    }
}

So, now, if you were to access your "Position" Create / Edit form, you'd have a nice "Add New" button where you could add a collection of PositionUnion entities through a nice inline table mechanism.

HOWEVER... there may be work you have to do related to *persisting* the relationships. In fact, in any one to many case with sonata_type_collection, I've had to do something like this:

class Position extends BaseAdmin {

    public function prePersist($position)
    {
        // fix weird bug with setter object not being call
        $position->setPositionUnions($position->getPositionUnions());
    }

    public function preUpdate($position)
    {
        // fix weird bug with setter object not being call
        $position->setPositionUnions($position->getPositionUnions());
    }

}

And then I have to implement that setter in the entity:

class Position
{
    public function setPositionUnions($positionUnions)
    {
        $this->positionUnions = new ArrayCollection();

        foreach ($positionUnions as $positionUnion) {
            $positionUnion->setPosition($this);
            $this->addPositionUnion($positionUnion);
        }
    }

}

Hope this helps!

Gabe


--
You received this message because you are subscribed to the Google Groups "sonata-users" group.
To view this discussion on the web visit https://groups.google.com/d/msg/sonata-users/-/QHfcHus3Sk8J.
To post to this group, send email to sonata...@googlegroups.com.
To unsubscribe from this group, send email to sonata-users...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/sonata-users?hl=en.



--


400 S. Crenshaw Blvd. Suite 100, Torrance, CA 90503

Connect with Us!     Twitter  |  LinkedIn

Stefano Alletti

unread,
Jan 28, 2013, 6:16:11 AM1/28/13
to sonata...@googlegroups.com
Hi Gabriel, 

i try your solution, but when i click in "Add New" button i have a error: 

"No entity manager defined for class Doctrine\Common\Collections\ArrayCollection (500 Internal Server Error)"

any idea?

Thanks, 

Stefano

bruno roux

unread,
Jan 28, 2013, 11:08:18 AM1/28/13
to sonata...@googlegroups.com
Hi,

are you sure to use the correct form field type ?

I managed to make it work with the following :

An Article Entity
An Image Entity
An ArticleImage Entity (used to manage the relations between Article and Image)

In my ArticleImageAdmin class I use the 'sonata_type_model_list' field type :

class ArticleImageAdmin extends Admin
{
  protected $baseRoutePattern = 'articleimage';


  protected function configureFormFields(FormMapper $formMapper)
  {

    $formMapper
        ->add('image', 'sonata_type_model_list', array('required' => false,), array('link_parameters' => array('provider' => 'sonata.media.provider.image', 'context' => 'article')))
        ->add('position');
  }
}

In ArticleAdmin however I use 'sonata_type_collection' :

class ArticleAdmin extends Admin
{
  public function configureFormFields(FormMapper $formMapper)
  {
    $formMapper
      ->with('General')
        ->add('surtitre', null, array('required' => false))
        ->add('titre')
        ->add('soustitre', null, array('required' => false))
        ->add('contenu', null, array('required' => false))
        ->add('taxonomies', null, array('property' => 'nom'))
        ->add('gratuit', null, array('required' => false))
        ->add('publie', null, array('required' => false))
      ->end()
      ->with('Supplementaire')
        ->add('source', null, array('required' => false))
        ->add('codeSource', null, array('required' => false))
        ->add('signature', null, array('required' => false))
        ->add('pageLogique', null, array('required' => false))
      ->end()
      ->with('Médias')
        ->add('images', 'sonata_type_collection', array(
            'required' => false,
            'by_reference' => false,

          ), array(
            'edit' => 'inline',
            'inline' => 'table',
            'sortable' => 'position',
          )
        )
      ->end()
    ;
  }
}


Let me know if everything is ok.

Gabriel Richards

unread,
Jan 28, 2013, 4:33:34 PM1/28/13
to sonata...@googlegroups.com
Hi Stefano.

Ya, you're using the wrong form field type... probably you're using sonata_type_model or sonata_type_model_list when you should be using sonata_type_collection.

Gabe


Stefano Alletti

unread,
Jan 29, 2013, 4:27:02 AM1/29/13
to sonata...@googlegroups.com
Hi Bruno, 

can you show me your Entities code? 

Thanks, 

Stefano

Stefano Alletti

unread,
Jan 29, 2013, 6:16:02 AM1/29/13
to sonata...@googlegroups.com
Hi guys, 

First thank you for help, now work it. 

I paste my code here, may be some one will have de same problem and can halp: 

Entities: Page, PageHasMedia, Media (SonataAdminBundle)

1) Override Media Entity

<?php

namespace Application\Sonata\MediaBundle\Entity;

use Sonata\MediaBundle\Entity\BaseMedia as BaseMedia;


/**
 * @ORM\Entity
 * @ORM\Table(name="media")
 */
class Media extends BaseMedia
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;
    /**
     * @ORM\OneToMany(targetEntity="\MyApp\ServicesBundle\Entity\PageHasMedia", mappedBy="id_media" , cascade={"all"})
     */
    protected $page_image;    
    
    
    /**
     * Get id
     *
     * @return integer $id
     */
    public function getId()
    {
        return $this->id;
    }
    
/**
     * Add page_image
     *
     * @param \MyApp\ServicesBundle\Entity\PageHasMedia $pageImage
     * @return Page
     */
    public function addPageImage(\MyApp\ServicesBundle\Entity\PageHasMedia $pageImage)
    {
        $this->page_image[] = $pageImage;
    
        return $this;
    }

    /**
     * Remove page_image
     *
     * @param \MyApp\ServicesBundle\Entity\PageHasMedia $pageImage
     */
    public function removePageImage(\MyApp\ServicesBundle\Entity\PageHasMedia $pageImage)
    {
        $this->page_image->removeElement($pageImage);
    }

    /**
     * Get page_image
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getPageImage()
    {
        return $this->page_image;
    }    
    
}


2) Page Entity

<?php

namespace MyApp\ServicesBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Gedmo\Mapping\Annotation as Gedmo; // gedmo annotations

/**
 * @ORM\Entity
 * @ORM\Table(name="page")
 */

class Page {

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

       ............ other properties ------

    /**
     * @ORM\OneToMany(targetEntity="PageHasMedia", mappedBy="id_page" , cascade={"all"})
     */
    protected $page_image;

   /**
     * Add page_image
     *
     * @param \MyApp\ServicesBundle\Entity\PageHasMedia $pageImage
     * @return Page
     */
    public function addPageImage(\MyApp\ServicesBundle\Entity\PageHasMedia $pageImage) {
        $this->page_image[] = $pageImage;

        return $this;
    }

    /**
     * Remove page_image
     *
     * @param \MyApp\ServicesBundle\Entity\PageHasMedia $pageImage
     */
    public function removePageImage(\MyApp\ServicesBundle\Entity\PageHasMedia $pageImage) {
        $this->page_image->removeElement($pageImage);
    }

    /**
     * Get page_image
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getPageImage() {
        return $this->page_image;
    }

    public function setPageImage($page_images) {
        $this->page_image = new ArrayCollection();

        foreach ($page_images as $page_image) {
            $page_image->setIdPage($this);
            $this->addPageImage($page_image);
        }
    }
}

3) PageAsMedia Entity

<?php

namespace MyApp\ServicesBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Gedmo\Mapping\Annotation as Gedmo; // gedmo annotations


/**
 * @ORM\Entity
 * @ORM\Table(name="page_media")
 */
class PageHasMedia {

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     *  @ORM\ManyToOne(targetEntity="Page", inversedBy="page_image", cascade={"all"})
     */
    protected $id_page;

    /**
     *  @ORM\ManyToOne(targetEntity="\Application\Sonata\MediaBundle\Entity\Media", inversedBy="page_image", cascade={"all"})
     */
    private $id_media;       
    
    public function __construct() {

    }    

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

    /**
     * Set id_page
     *
     * @param \MyApp\ServicesBundle\Entity\Page $idPage
     * @return PageHasMedia
     */
    public function setIdPage(\MyApp\ServicesBundle\Entity\Page $idPage = null)
    {
        $this->id_page = $idPage;
    
        return $this;
    }

    /**
     * Get id_page
     *
     * @return \MyApp\ServicesBundle\Entity\Page 
     */
    public function getIdPage()
    {
        return $this->id_page;
    }

    /**
     * Set id_media
     *
     * @param \MyApp\ServicesBundle\Entity\Media $idMedia
     * @return PageHasMedia
     */
    public function setIdMedia(\Application\Sonata\MediaBundle\Entity\Media $idMedia = null)
    {
        $this->id_media = $idMedia;
    
        return $this;
    }

    /**
     * Get id_media
     *
     * @return \Application\Sonata\MediaBundle\Entity\Media 
     */
    public function getIdMedia()
    {
        return $this->id_media;
    }
}

4) PageAdmin Class

<?php

namespace MyApp\ServicesBundle\Admin;

use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use MyApp\ServicesBundle\Entity\Page;

class PageAdmin extends Admin {

    protected $datagridValues = array(
        '_sort_order' => 'DESC',
        '_sort_by' => 'created_at'
    );

    protected function configureListFields(ListMapper $listMapper) {
        $listMapper
             .....
        ;
    }

    protected function configureFormFields(FormMapper $formMapper) {
        $formMapper
                ....
                ->add('page_image', 'sonata_type_collection', array(
                    'cascade_validation' => true,
                        ), array(
                    'edit' => 'inline',
                    'inline' => 'table',
                    'sortable' => 'position',
                    'link_parameters' => array('context' => 'default'),
                        )
                )
                .......
        ;
    }

    protected function configureDatagridFilters(DatagridMapper $datagridMapper) {
        ......
    }

    public function prePersist($page) {
       
        $page->setPageImage($page->getPageImage());
    }

    public function preUpdate($page) {
       
        $page->setPageImage($page->getPageImage());
    }

5) PageHasMediaAdmin Class

<?php

namespace MyApp\ServicesBundle\Admin;

use Sonata\MediaBundle\Admin\GalleryHasMediaAdmin as BaseAdmin;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Validator\ErrorElement;

class PageHasMediaAdmin extends BaseAdmin {

    protected function configureFormFields(FormMapper $formMapper) {

        $formMapper
                ->add('id_media', 'sonata_type_model_list', array('required' => false,), array('link_parameters' => array('provider' => 'sonata.media.provider.image', 'context' => 'default')))
                ;

Marc Juchli

unread,
Feb 10, 2013, 4:02:12 PM2/10/13
to sonata...@googlegroups.com
Hello guys,

Seems like I'm fighting with more or less the same problem.

I've a magazine entity where I want to get a Gallery (Sonata Media Bundle) liked to. So that every magazine can have a gallery of pictures --> ManyToOne

/**
 * Magazine
 *
 * @ORM\Table(name="magazine")
 * @ORM\Entity
 */
class Magazine
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    ....

    /**
     * @ORM\ManyToOne(targetEntity="Application\Sonata\MediaBundle\Entity\Gallery", inversedBy="magazine", cascade={"persist"})
     */
    private $gallery;

    ....


class Gallery extends BaseGallery
{

    /**
     * @var integer $id
     */
    protected $id;

    /**
     * @var Collection
     *
     * @ORM\OneToMany(targetEntity="***\***\MagazineBundle\Entity\Magazine", mappedBy="gallery", cascade={"persist"})
     *
     */
    private $magazine;

    ....

I do inline edition with sonata_type_model_list which works perfect.

class MagazineAdmin extends Admin
{
    /**
     * {@inheritdoc}
     */
    protected function configureFormFields(FormMapper $formMapper)
    {
        $formMapper
        ->with('Cover')
            ->add('gallery', 'sonata_type_model_list', array(
                'by_reference' => false,
                'required' => false
            ), array(
                'edit' => 'inline',
                'inline' => 'table',
                'sortable'  => 'position',
            ))
        ->end()
    ...

The problem is now that I get 

Entities passed to the choice field must be managed

After submitting the magazine including a gallery.

Would be thankful for some hints.

Cheers, Marc

Nelson Suniaga

unread,
Feb 10, 2013, 10:52:45 PM2/10/13
to sonata...@googlegroups.com
I'm stucked in almost the same situation that you, but I've never had that error message. Even so, did you registered the service MagazineAdmin? If so, publish the file to see it.

Marc Juchli

unread,
Feb 11, 2013, 4:15:27 AM2/11/13
to sonata...@googlegroups.com
Hi Nelson,

Am Montag, 11. Februar 2013 04:52:45 UTC+1 schrieb Nelson Suniaga:
I'm stucked in almost the same situation that you, but I've never had that error message. Even so, did you registered the service MagazineAdmin? If so, publish the file to see it.


Yes I did, so here's my service:
    <services>
        <service id="codag.du.admin.magazine" class="Codag\Du\MagazineBundle\Admin\MagazineAdmin">
            <tag name="sonata.admin" manager_type="orm" group="Magazin" label="Ausgabe" />

            <argument />
            <argument>Codag\Du\MagazineBundle\Entity\Magazine</argument>
            <argument />
        </service>
    </services> 

Nelson Jesus Suniaga Romero

unread,
Feb 11, 2013, 5:27:06 AM2/11/13
to sonata...@googlegroups.com
That error message seems to be pretty tricky. Take a look to this StackOverflow post and let me know if it works in your case.

--
You received this message because you are subscribed to the Google Groups "sonata-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sonata-users...@googlegroups.com.

To post to this group, send email to sonata...@googlegroups.com.

Nelson Jesus Suniaga Romero

unread,
Feb 11, 2013, 5:29:17 AM2/11/13
to sonata...@googlegroups.com

Marc Juchli

unread,
Feb 12, 2013, 4:51:36 AM2/12/13
to sonata...@googlegroups.com, nelson...@gmail.com
Hi Nelson,

Thanks for the link. But it seems like I've got a different problem as this guy has. I got the issue while "reading" the record A but saving works fine.

I think the error must be something like sonata doesn't know how to deal with the given data...
Any ideas how to transform?

Marc

Marc Juchli

unread,
Feb 12, 2013, 5:09:31 AM2/12/13
to sonata...@googlegroups.com, nelson...@gmail.com
Ok I got it.
"'by_reference' => false" was the issue here. Set to true and works great.

Thanks for the support.

Nelson Jesus Suniaga Romero

unread,
Feb 12, 2013, 5:36:01 AM2/12/13
to sonata...@googlegroups.com
I was about to post another message on this thread.

Just some hours ago I got the same error than you, because I'm working in embed forms I guess and the only thing I did was to delete the cache, but not by running app/console cache:clear, but running rm -rfv app/cache/* instead (very risky, by the way), and the error message just dissapeared.

I know it's a dumb question but, did you try clearing the cache?.

--

Marc Juchli

unread,
Feb 12, 2013, 9:29:58 AM2/12/13
to sonata...@googlegroups.com, nelson...@gmail.com
Hi Nelson,


Am Dienstag, 12. Februar 2013 11:36:01 UTC+1 schrieb Nelson Suniaga:
I was about to post another message on this thread.

Just some hours ago I got the same error than you, because I'm working in embed forms I guess and the only thing I did was to delete the cache, but not by running app/console cache:clear, but running rm -rfv app/cache/* instead (very risky, by the way), and the error message just dissapeared.

I know it's a dumb question but, did you try clearing the cache?.


I did a couple of times :)
The error occurred due to by_reference (see my post above).

Cheers, Marc

Reynier Pérez Mira

unread,
Feb 27, 2014, 7:21:46 PM2/27/14
to sonata...@googlegroups.com, nelson...@gmail.com
Hi every one, I read the complete post and try to solve mi problems that pretty much is same as yours with the only difference I'll not upload images instead I'll upload documents. This is what I did:

1) Override \Application\Sonata\MediaBundle\Entity\Media.php

<?php

namespace Application\Sonata\MediaBundle\Entity;

use Sonata\MediaBundle\Entity\BaseMedia as BaseMedia;
use Doctrine\ORM\Mapping as ORM;

class Media extends BaseMedia {

    /**
     * @var integer $id
     */
    protected $id;

    /**
     * @ORM\ManyToMany(targetEntity="PL\OrderBundle\Entity\Order", mappedBy="medias")
     */
    protected $orders;

    /**
     * @var Collection
     * @ORM\OneToMany(targetEntity="PL\OrderBundle\Entity\OrderHasMedia", mappedBy="media", cascade={"persist"})
     *
     */
    private $orderDocs;

    public function __construct() {
        $this->orders = new \Doctrine\Common\Collections\ArrayCollection();
    }

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

    /**
     * @param \PL\OrderBundle\Entity\OrderHasMedia $orderDocs
     * @return Document
     */
    public function addOrdersDocs(\PL\OrderBundle\Entity\OrderHasMedia $orderDocs) {
        $this->orderDocs[] = $orderDocs;

        return $this;
    }

    /**
     * @param \PL\OrderBundle\Entity\OrderHasMedia $orderDocs
     */
    public function removeOrderDocs(\PL\OrderBundle\Entity\OrderHasMedia $orderDocs) {
        $this->orderDocs->removeElement($orderDocs);
    }

    /**
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getOrdersDocs() {
        return $this->orderDocs;
    }

}

2) Write \PL\OrderBundle\Entity\Order.php entity:

<?php

namespace PL\OrderBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use PL\OrderBundle\DBAL\Types\ChargeStatusType;
use PL\OrderBundle\DBAL\Types\TransportMediaType;
use PL\OrderBundle\DBAL\Types\IncotermType;
use Fresh\Bundle\DoctrineEnumBundle\Validator\Constraints as DoctrineAssert;
use Symfony\Component\Validator\Constraints as Assert;
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * @ORM\Entity
 * @ORM\Table(name="tb_order")
 * @Gedmo\Loggable
 */
class Order {

    public static $chargeStatusList = array(
        "0" => "Ninguno",
        "1" => "Proceso de Fabricación",
        "2" => "Pickup en destino",
        "3" => "A la espera de recojo por cliente",
        "4" => "Carga en tránsito",
        "5" => "Carga arribada",
        "6" => "En proceso de aduana",
        "7" => "Entregado a cliente",
        "8" => "En bodega"
    );
    public static $incotermList = array(
        "0" => "Ninguno",
        "1" => "EWX",
        "2" => "FOB",
        "3" => "CIF",
        "4" => "DDP"
    );
    public static $transportMediaList = array(
        "0" => "EXW",
        "1" => "Maritimo",
        "2" => "Aereo"
    );

    /**
     * @ORM\Id
     * @ORM\Column(type="string", length=15, unique=true, nullable=false)
     * @Gedmo\Versioned
     */
    protected $no_order;

    /**
     * @ORM\ManyToOne(targetEntity="\PL\CompanyBundle\Entity\Company", inversedBy="orders")
     * @Gedmo\Versioned
     */
    protected $company;

    /**
     * @ORM\Column(type="string", length=15, unique=true)
     * @Gedmo\Versioned
     */
    protected $business_case;

    /**
     * @DoctrineAssert\Enum(entity="ChargeStatusType")
     * @ORM\Column(name="charge_status", type="ChargeStatusType", nullable=false)
     * @Gedmo\Versioned
     */
    protected $charge_status;

    /**
     * @ORM\Column(type="date")
     * @Gedmo\Versioned
     */
    protected $eta;

    /**
     * @ORM\Column(type="date")
     * @Gedmo\Versioned
     */
    protected $etd;

    /**
     * @DoctrineAssert\Enum(entity="TransportMediaType")
     * @ORM\Column(name="transport_media", type="TransportMediaType", nullable=false)
     * @Gedmo\Versioned
     */
    protected $transport_media;

    /**
     * @DoctrineAssert\Enum(entity="IncotermType")
     * @ORM\Column(name="incoterm", type="IncotermType", nullable=false)
     * @Gedmo\Versioned
     */
    protected $incoterm;

    /**
     * @ORM\OneToMany(targetEntity="\PL\OrderBundle\Entity\OrderHasComment", mappedBy="no_order")
     * @Gedmo\Versioned
     */
    protected $comment;

    /**
     * @ORM\ManyToMany(targetEntity="\Application\Sonata\MediaBundle\Entity\Media", cascade={"all"})
     * @ORM\JoinTable(name="order_has_media__media",
     *      joinColumns={@ORM\JoinColumn(name="order_no_order", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="media__media_id", referencedColumnName="id", onDelete="CASCADE")})
     */
    protected $documents;

    /**
     * @var Collection
     * @ORM\OneToMany(targetEntity="PL\OrderBundle\Entity\OrderHasMedia", mappedBy="order", cascade={"persist"})
     */
    protected $orderDocs;

    public function __construct() {
        $this->documents = new \Doctrine\Common\Collections\ArrayCollection();
    }

    public function setNoOrder($no_order) {
        $this->no_order = $no_order;
    }

    public function getNoOrder() {
        return $this->no_order;
    }

    public function setCompany(Company $company) {
        $this->company = $company;
    }

    public function getCompany() {
        return $this->company;
    }

    public function setBusinessCase($business_case) {
        $this->business_case = $business_case;
    }

    public function getBusinessCase() {
        return $this->business_case;
    }

    public function setChargeStatus($charge_status) {
        $this->charge_status = $charge_status;
    }

    public function getChargeStatus() {
        return $this->charge_status;
    }

    public function setETA($eta) {
        $this->eta = $eta;
    }

    public function getETA() {
        return $this->eta;
    }

    public function setETD($etd) {
        $this->etd = $etd;
    }

    public function getETD() {
        return $this->etd;
    }

    public function setTransportMedia($transport_media) {
        $this->transport_media = $transport_media;
    }

    public function getTransportMedia() {
        return $this->transport_media;
    }

    public function setIncoterm($incoterm) {
        $this->incoterm = $incoterm;
    }

    public function getIncoterm() {
        return $this->incoterm;
    }

    public function setComment(\PL\OrderBundle\Entity\OrderHasComment $comment) {
        $this->comment = $comment;
    }

    public function getComment() {
        return $this->comment;
    }

    public function setDocuments(\Application\Sonata\MediaBundle\Entity\Media $documents) {
        $this->documents[] = $documents;
    }

    public function getDocuments() {
        return $this->documents;
    }

    /**
     * @param \PL\OrderBundle\Entity\OrderHasMedia $orderDocs
     * @return Document
     */
    public function addOrdersDocs(\PL\OrderBundle\Entity\OrderHasMedia $orderDocs) {
        $this->orderDocs[] = $orderDocs;

        return $this;
    }

    /**
     * @param \PL\OrderBundle\Entity\OrderHasMedia $orderDocs
     */
    public function removeOrderDocs(\PL\OrderBundle\Entity\OrderHasMedia $orderDocs) {
        $this->orderDocs->removeElement($orderDocs);
    }

    /**
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getOrdersDocs() {
        return $this->orderDocs;
    }

    public function setOrderDocs($orderDocs) {
        $this->orderDocs = new ArrayCollection();

        foreach ($orderDocs as $orderDoc) {
            $orderDoc->setOrder($this);
            $this->addOrdersDocs($orderDoc);
        }
    }

    public function getChargeStatusValue() {
        return self::$chargeStatusList[$this->charge_status];
    }

    public function getIncotermValue() {
        return self::$incotermList[$this->incoterm];
    }

    public function getTransportMediaValue() {
        return self::$transportMediaList[$this->transport_media];
    }

}
 
3) Write \PL\OrderBundle\Entity\OrderHasMedia.php

<?php

namespace PL\OrderBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * @ORM\Entity
 * @ORM\Table(name="order_has_media__media")
 */
class OrderHasMedia {

    /**
     * @var Order
     * @ORM\ManyToOne(targetEntity="PL\OrderBundle\Entity\Order", inversedBy="orderDocs", cascade={"persist"})
     */
    private $order;

    /**
     * @var Media
     * @ORM\ManyToOne(targetEntity="Application\Sonata\MediaBundle\Entity\Media", inversedBy="orderDocs", cascade={"persist"})
     */
    private $media;

    /**
     * @param \PL\OrderBundle\Entity\Order $order
     * @return PageHasMedia
     */
    public function setOrder(\PL\OrderBundle\Entity\Order $order = null) {
        $this->order = $order;
        return $this;
    }

    /**
     * Get id_page
     *
     * @return \PL\OrderBundle\Entity\Order
     */
    public function getOrder() {
        return $this->order;
    }

    /**
     * Set id_media
     *
     * @param Application\Sonata\MediaBundle\Entity\Media $media
     * @return PageHasMedia
     */
    public function setMedia(\Application\Sonata\MediaBundle\Entity\Media $media = null) {
        $this->media = $media;
        return $this;
    }

    /**
     * @return \Application\Sonata\MediaBundle\Entity\Media
     */
    public function getMedia() {
        return $this->media;
    }

}

4) Add proper code to \PL\OrderBundle\Admin\OrderAdmin.php

    protected function configureFormFields(FormMapper $form) {
        $form
                ->add('no_order', null, array('label' => 'No. Order'))
                ->add('company', 'entity', array('class' => 'PL\CompanyBundle\Entity\Company', 'label' => 'Cliente'))
                ->add('business_case', null, array('label' => 'BC'))
                ->add('charge_status', 'choice', array('choices' => Order::$chargeStatusList, "empty_value" => "Seleccione una opción", "required" => true, 'label' => 'Estado de la carga'))
                ->add('eta', 'date', array('label' => 'ETA', 'widget' => 'single_text', 'required' => false, 'attr' => array('class' => 'datepicker')))
                ->add('etd', 'date', array('label' => 'ETD', 'widget' => 'single_text', 'required' => false, 'attr' => array('class' => 'datepicker')))
                ->add('transport_media', 'choice', array('choices' => Order::$transportMediaList, "empty_value" => "Seleccione una opción", "required" => true, 'label' => 'Via de Transporte'))
                ->add('incoterm', 'choice', array('choices' => Order::$incotermList, "empty_value" => "Seleccione una opción", "required" => true, 'label' => 'Incoterm'))
                ->add('orderDocs', 'sonata_type_collection', array(
                    'cascade_validation' => true,
                        ), array(
                    'edit' => 'inline',
                    'inline' => 'table',
                    'sortable' => 'position',
                    'link_parameters' => array('context' => 'default')
        ));
    }

    public function prePersist($doc) {
        $doc->setOrderDocs($doc->getOrdersDocs());
    }

    public function preUpdate($doc) {
        $doc->setOrderDocs($doc->getOrdersDocs());
    }

5) Add proper code to \PL\OrderBundle\Admin\OrderHasMediaAdmin.php

<?php

/**
 * Description of OrderAdmin
 */

namespace PL\OrderBundle\Admin;

use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Show\ShowMapper;

class OrderHasMediaAdmin extends Admin {

    protected function configureFormFields(FormMapper $formMapper) {
        function configureFormFields(FormMapper $formMapper) {
            $formMapper->add('media', 'sonata_type_model_list', array('required' => false,), array('link_parameters' => array('provider' => 'sonata.media.provider.file', 'context' => 'default')));
        }

    }

}

But I get this error when I access "CREATE" action:

RuntimeException: The current field `orderDocs` is not linked to an admin. Please create one for the target entity : `PL\OrderBundle\Entity\OrderHasMedia`


Any advice or help? I stucked on this!!! :-(

Cassiano Tartari

unread,
Feb 28, 2014, 6:10:36 AM2/28/14
to sonata-users, nelson...@gmail.com

On Thu, Feb 27, 2014 at 9:21 PM, Reynier Pérez Mira <reyn...@gmail.com> wrote:
->add('orderDocs', 'sonata_type_collection',

I've just read the error and search for OrderDocs, you've pasted to much code, nobody has time and patience to read all your code, understand your particularly code, that's why nobody answer before.

But seems that you didn't wrote an admin class for OrderDocs, if I'm not wrong the 'sonata_type_collection' show a list with the registers and give you a interface to add some if necessary, this list, edit and what else comes from and admin class.

http://sonata-project.org/bundles/doctrine-orm-admin/master/doc/tutorial/creating_your_first_admin_class/defining_admin_class.html#defining-admin-class


Cassiano Valle Tartari
MSc. Computer Engineer

Tel: +55.48.84474818
Email: fal...@cassianotartari.eng.br
Site: http://www.cassianotartari.eng.br

QR Code

reyn...@gmail.com

unread,
Feb 28, 2014, 9:35:24 AM2/28/14
to sonata...@googlegroups.com, cassian...@gmail.com, nelson...@gmail.com

Cassiano, that was my bad but sometimes users ask for code for that reason I post the whole code but here I go again just with relevant code and better explanation hoping you can help me. Forget about the previous post since I refactored my code once again following @Stefano Alleti solution at https://groups.google.com/forum/#!topic/sonata-users/2yttN7sXPhM and in order to leave my code more readable and user friendly I remove unwanted parts:

Entity: \PL\OrderBundle\Entity\Order.php

/**
 * @ORM\Entity
 * @ORM\Table(name="tb_order")
 */
class Order {
  ...

    /**
     * @ORM\OneToMany(targetEntity="PL\OrderBundle\Entity\OrderHasMedia", mappedBy="order" , cascade={"all"})
     */
    protected $order_document;

    public function addOrderDocument(\PL\OrderBundle\Entity\OrderHasMedia $orderDocument) {
        $this->order_document[] = $orderDocument;
        return $this;
    }

    public function removeOrderDocument(\PL\OrderBundle\Entity\OrderHasMedia $orderDocument) {
        $this->order_document->removeElement($orderDocument);
    }

    public function getOrderDocument() {
        return $this->order_document;
    }

    public function setOrderDocument($order_document) {
        $this->order_document = new ArrayCollection();

        foreach ($order_document as $od) {
            $od->setOrder($this);
            $this->addOrderDocument($od);
        }
    }

}

Entity: \Application\Sonata\MediaBundle\Entity\Media.php

class Media extends BaseMedia {

    /**
     * @var integer $id
     */
    protected $id;

    /**
     * @var Collection
     * @ORM\OneToMany(targetEntity="PL\OrderBundle\Entity\OrderHasMedia", mappedBy="media", cascade={"all"})
     *
     */
    protected $order_document;

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

    public function addOrderDocument(\PL\OrderBundle\Entity\OrderHasMedia $orderDocument) {
        $this->order_document[] = $orderDocument;
        return $this;
    }

    public function removeOrderDocument(\PL\OrderBundle\Entity\OrderHasMedia $orderDocument) {
        $this->order_document->removeElement($orderDocument);
    }

    public function getOrderDocument() {
        return $this->order_document;
    }

}

Entity: \PL\OrderBundle\Entity\OrderHasMedia.php

/**
 * @ORM\Entity
 * @ORM\Table(name="order_has_media__media")
 */
class OrderHasMedia {

    /**
     * @var Order
     * @ORM\ManyToOne(targetEntity="PL\OrderBundle\Entity\Order", inversedBy="order_document", cascade={"all"})
     */
    private $order;

    /**
     * @var Media
     * @ORM\ManyToOne(targetEntity="Application\Sonata\MediaBundle\Entity\Media", inversedBy="order_document", cascade={"all"})
     */
    private $media;

    public function setOrder(\PL\OrderBundle\Entity\Order $order = null) {
        $this->order = $order;
        return $this;
    }

    public function getOrder() {
        return $this->order;
    }

    public function setMedia(\Application\Sonata\MediaBundle\Entity\Media $media = null) {
        $this->media = $media;
        return $this;
    }

    public function getMedia() {
        return $this->media;
    }

}

Class: \PL\OrderBundle\Admin\OrderAdmin.php

class OrderAdmin extends Admin {

    protected function configureFormFields(FormMapper $form) {
        $form
               ....
                ->add('order_document', 'sonata_type_collection', array(
                    'cascade_validation' => true,
                    'required' => false,
                    'by_reference' => false,
                        ), array(
                    'edit' => 'inline',
                    'inline' => 'table',
                    'sortable' => 'position',
                    'link_parameters' => array('context' => 'default'
        )));
    }

    public function prePersist($order_document) {
        $order_document->setOrderDocument($order_document->getOrderDocument());
    }
    
    public function preUpdate($order_document) {
        $order_document->setOrderDocument($order_document->getOrderDocument());
    }

}

Class "\PL\OrderBundle\Admin\OrderHasMediaAdmin.php"

class OrderHasMediaAdmin extends Admin {

    protected function configureFormFields(FormMapper $form) {
        $form->add('media', 'sonata_type_model_list', array('required' => false,), array('link_parameters' => array('provider' => 'sonata.media.provider.file', 'context' => 'default')));
    }
}

But I got the same error once again:

RuntimeException: The current field `order_document` is not linked to an admin. Please create one for the target entity : `PL\OrderBundle\Entity\OrderHasMedia`

I tried changing OrderHasMediaAdmin.php to OrderDocumentAdmin.php but without success, what is wrong in my code? Do I need to register OrderHasMediaAdmin in my config.yml as I do for others? In that case how I do not display this entity in menus or blocks?

Cheers and hoping you can give me some help Cassiano :-D

ReynierPM
Mobile: +58 424-180.56.09


--
You received this message because you are subscribed to the Google Groups "sonata-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sonata-users...@googlegroups.com.
To post to this group, send email to sonata...@googlegroups.com.

Cassiano Tartari

unread,
Feb 28, 2014, 9:45:57 AM2/28/14
to reyn...@gmail.com, sonata-users, nelson...@gmail.com
So, $order_document is an PL\OrderBundle\Entity\OrderHasMedia

Yes, you should create an admin class to OrderHasMedia and register as a service like others and you need add in the service the fallow tag: show_in_dashboard: false to not display in menu/blocks.

    company.order.admin.orderhasmedia:
        class: PL\OrderBundle\Admin\OrderHasMediaAdmin
        arguments: [null, PL\OrderBundle\Entity\OrderHasMedia, 'SonataAdminBundle:CRUD']
        tags:
            - { show_in_dashboard: false, name: sonata.admin, manager_type: orm, group: group_order, label: menu_order_has_media, label_translator_strategy: sonata.admin.label.strategy.underscore, label_catalogue: OrderBundle }


Cassiano Valle Tartari
MSc. Computer Engineer

Tel: +55.48.84474818
Email: fal...@cassianotartari.eng.br
Site: http://www.cassianotartari.eng.br

QR Code


reyn...@gmail.com

unread,
Feb 28, 2014, 9:52:19 AM2/28/14
to Cassiano Tartari, sonata-users, nelson...@gmail.com

On Fri, Feb 28, 2014 at 10:15 AM, Cassiano Tartari <cassian...@gmail.com> wrote:
company.order.admin.orderhasmedia:
        class: PL\OrderBundle\Admin\OrderHasMediaAdmin
        arguments: [null, PL\OrderBundle\Entity\OrderHasMedia, 'SonataAdminBundle:CRUD']
        tags:
            - { show_in_dashboard: false, name: sonata.admin, manager_type: orm, group: group_order, label: menu_order_has_media, label_translator_strategy: sonata.admin.label.strategy.underscore, label_catalogue: OrderBundle }

EXCELLENTTTTTTT!!! You rock man!! You save my day. One more thing where I change the "form.label_media" at table and also how I hide the "List" button? See attached image for info

ReynierPM
Mobile: +58 424-180.56.09
1.png

Cassiano Tartari

unread,
Feb 28, 2014, 10:59:40 AM2/28/14
to reyn...@gmail.com, sonata-users, Nelson Suniaga
I'm happy to know that it works!

Probably you don't have the translation for this "form.label_media" try to add in the translation files.

To hide the list button just add the follow in your admin class:

use Sonata\AdminBundle\Route\RouteCollection;

    protected function configureRoutes(RouteCollection $collection) {
        $collection->remove('list');
    }

reyn...@gmail.com

unread,
Feb 28, 2014, 12:10:50 PM2/28/14
to Cassiano Tartari, sonata-users, Nelson Suniaga
On Fri, Feb 28, 2014 at 11:29 AM, Cassiano Tartari <cassian...@gmail.com> wrote:
I'm happy to know that it works!


Happiness is not for eternity :-( since I get another issue. I attached to images to the post. In 1.png you can see the file because I hit "Agregar nuevo" button and upload a file. Now I want to delete that file so I check the option and hit "Borrar" button and file name disappear but the row still there when it shouldn't be since I delete "apparently" the file, is that behavior right? Or I miss something else?
 
Probably you don't have the translation for this "form.label_media" try to add in the translation files.

I copied the file SonataMediaBundle.es.xliff from \vendor\sonata-project\media-bundle\Sonata\MediaBundle\Resources\translations to \Application\Sonata\MediaBundle\Resources\translations delete all it content and add this line:

<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
    <file source-language="en" target-language="es" datatype="plaintext" original="SonataMediaBundle.en.xliff">
        <body>
            <trans-unit id="form.label_media">
                <source>form.label_media</source>
                <target>Documento</target>
            </trans-unit>
        </body>
    </file>
</xliff>

Doesn't work. Once again I miss something?
 
To hide the list button just add the follow in your admin class:

use Sonata\AdminBundle\Route\RouteCollection;

    protected function configureRoutes(RouteCollection $collection) {
        $collection->remove('list');
    }

Thanks I'll test this one later

ReynierPM
Mobile: +58 424-180.56.09
1.png
2.png

reyn...@gmail.com

unread,
Feb 28, 2014, 12:13:04 PM2/28/14
to Cassiano Tartari, sonata-users, Nelson Suniaga

On Fri, Feb 28, 2014 at 12:40 PM, reyn...@gmail.com <reyn...@gmail.com> wrote:
On Fri, Feb 28, 2014 at 11:29 AM, Cassiano Tartari <cassian...@gmail.com> wrote:
I'm happy to know that it works!


Happiness is not for eternity :-( since I get another issue. I attached to images to the post. In 1.png you can see the file because I hit "Agregar nuevo" button and upload a file. Now I want to delete that file so I check the option and hit "Borrar" button and file name disappear but the row still there when it shouldn't be since I delete "apparently" the file, is that behavior right? Or I miss something else?

Also regarding this same error, file is not deleted physically it's just removed from form and I want, if it's possible, to delete complete the file and the tables rows, how?

ReynierPM
Mobile: +58 424-180.56.09

Cassiano Tartari

unread,
Feb 28, 2014, 12:24:52 PM2/28/14
to reyn...@gmail.com, sonata-users, Nelson Suniaga
On Fri, Feb 28, 2014 at 2:10 PM, reyn...@gmail.com <reyn...@gmail.com> wrote:

On Fri, Feb 28, 2014 at 11:29 AM, Cassiano Tartari <cassian...@gmail.com> wrote:
I'm happy to know that it works!


Happiness is not for eternity :-( since I get another issue. I attached to images to the post. In 1.png you can see the file because I hit "Agregar nuevo" button and upload a file. Now I want to delete that file so I check the option and hit "Borrar" button and file name disappear but the row still there when it shouldn't be since I delete "apparently" the file, is that behavior right? Or I miss something else?


public function removeUpload() with @ORM\PostRemove() and @ORM\HasLifecycleCallbacks

 
 
Probably you don't have the translation for this "form.label_media" try to add in the translation files.

I copied the file SonataMediaBundle.es.xliff from \vendor\sonata-project\media-bundle\Sonata\MediaBundle\Resources\translations to \Application\Sonata\MediaBundle\Resources\translations delete all it content and add this line:

<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
    <file source-language="en" target-language="es" datatype="plaintext" original="SonataMediaBundle.en.xliff">
        <body>
            <trans-unit id="form.label_media">
                <source>form.label_media</source>
                <target>Documento</target>
            </trans-unit>
        </body>
    </file>
</xliff>

Doesn't work. Once again I miss something?

reyn...@gmail.com

unread,
Feb 28, 2014, 12:40:18 PM2/28/14
to Cassiano Tartari, sonata-users, Nelson Suniaga
On Fri, Feb 28, 2014 at 12:54 PM, Cassiano Tartari <cassian...@gmail.com> wrote:


public function removeUpload() with @ORM\PostRemove() and @ORM\HasLifecycleCallbacks

Ok, I see it and this should remove the file but where I should write that code? I mean in which entity? Media?
  



I read your post and also this ones [1], [2] and [3] too but none works for me I clear also the cache/ folder and nothing does not work

Reply all
Reply to author
Forward
0 new messages