Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 583
caponica Avatar asked Aug 07 '13 14:08

caponica


2 Answers

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

like image 77
caponica Avatar answered Nov 09 '22 17:11

caponica


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

like image 45
hombee Avatar answered Nov 09 '22 18:11

hombee