简体   繁体   中英

How to get child object in embedded Admin class in Sonata Admin?

I'm trying to get and manipulate the actual object related to a ImageAdmin class in SonataAdmin (using Symfony 2.3). This works fine when the ImageAdmin class is the only one being used. But when ImageAdmin is embedded in another Admin it goes horribly wrong.

Here's what works when you don't have embedded Admins:

class ImageAdmin extends Admin {
    protected $baseRoutePattern = 'image';

    protected function configureFormFields(FormMapper $formMapper) {
        $subject = $this->getSubject();
    }
}

But when you embed ImageAdmin in ParentAdmin using this:

class PageAdmin extends Admin {
    protected function configureFormFields(FormMapper $formMapper) {
        $formMapper->add('image1', 'sonata_type_admin');
    }
}

Then when you're editing a Parent item with id 10 and call getSubject() in ImageAdmin you get the Image with id 10!

In other words getSubject() extracts the id from the URL then calls $this->getModelManager()->find($this->getClass(), $id); , which cross-references the Parent id and the Image id. Oops!


So... what I want to do is be able to get hold of the actual object that is being rendered/edited in the current ImageAdmin instance, whether it's being edited directly or via an embedded form, and then be able to do things with it.

Maybe getSubject() is the wrong tree to be barking up, but I note that $this->getCurrentChild() returns false when called from ImageAdmin::configureFormFields(), even when that ImageAdmin is embedded using the sonata_type_admin field type. I'm quite confused...

Anyway, I hope it is possible to get hold of the object in some obvious way that I've overlooked and somebody here can help enlighten me!

Thanks to Tautrimas for some ideas, but I managed to figure out an answer to this:

In ImageAdmin set this:

protected function configureFormFields(FormMapper $formMapper)
{
    if($this->hasParentFieldDescription()) { // this Admin is embedded
        $getter = 'get' . $this->getParentFieldDescription()->getFieldName();
        $parent = $this->getParentFieldDescription()->getAdmin()->getSubject();
        if ($parent) {
          $image = $parent->$getter();
        } else {
          $image = null;
        }
    } else { // this Admin is not embedded
        $image = $this->getSubject();
    }

    // You can then do things with the $image, like show a thumbnail in the help:
    $fileFieldOptions = array('required' => false);
    if ($image && ($webPath = $image->getWebPath())) {
        $fileFieldOptions['help'] = '<img src="'.$webPath.'" class="admin-preview" />';
    }

    $formMapper
        ->add('file', 'file', $fileFieldOptions)
    ;
}

I'll post this in the upcoming SonataAdmin cookbook soon!

https://github.com/sonata-project/SonataAdminBundle/issues/1546

caponica's solution is working only on oneToOne relations, am I right? In my oneToMany case , this: $parent->$getter() returns a collection, and I don't know how to identify the current subject. I've found this bug report: https://github.com/sonata-project/SonataAdminBundle/issues/1568 , which contains a fix for this, but it is still open, so I hope they merge it soon:(

Edit

With some research there is a temporary fix for this: Fixed getting wrong subject in sonata_type_collection

In short:

create a class and copypaste the content of this file: AdminType then add this to your services.yml, and change the class namespace to you new class namespace:

services:
sonata.admin.form.type.admin:
    class: ACME\AdminBundle\Form\Type\AdminType
    tags:
        - { name: form.type, alias: sonata_type_admin }

It still has a bug though:

also fix doesn't work when enabled cascade_validation in the parent docment and embedded form has errors

Can you try $this->getForm()->getViewData(); within your ImageAdmin? This should get you the correct child entity.

I tried all these solutions, but none proved to work.
So, I worked to find a solution. My solution is based on caponica's solution, but work on oneToMany case. Tha solution I found is a workaround, but works good.
It's working using the session.

public function getCurrentObjectFromCollection($adminChild)
    {
    $getter = 'get' . $adminChild->getParentFieldDescription()
                               ->getFieldName();
    $parent = $adminChild->getParentFieldDescription()
                   ->getAdmin()
                   ->getSubject();
    $collection = $parent->$getter();

    $session = $adminChild->getRequest()->getSession();
    $number = 0;
    if ($session->get('adminCollection')) {
        $number = $session->get('adminCollection');
        $session->remove('adminCollection');
    }
    else {
        $session->set('adminCollection', 1 - $number);
    }

    return $collection[$number];
}

And you get the correct object in the admin by:

    $object = $this->getCurrentObjectFromCollection($this)

So, when the parent needs to show the list of child admins, each child admin will run this function and will update the session parameter. When all the elements have been taken, the session parameter is deleted.
This code is made for lists with only 2 elements, but can be updated for any number of elements.

Hope this helps somebody :)

I had same problem and i am able to do this through "Custom Form Type Extension" for which documentation is given on the link " http://symfony.com/doc/current/cookbook/form/create_form_type_extension.html " .

It is the perfect solution ..

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