php - Nested collection fields in Sonata Admin (2.3) -
i'm having problems creating form creating course. part of database scheme i'm trying create form:
so i'm trying create course can create sessions , dates (moment) attached session. should this:
in courseadmin class have:
protected function configureformfields(formmapper $formmapper) { $formmapper ->add('name', 'text', array('label' => 'naam')) ->add('description', 'textarea', array('label' => 'beschrijving')) ->add('materials', 'textarea', array('label' => 'benodigde materialen')) ->add('numberofparticipants', 'number', array('label' => 'aantal deelnembers')) ->add('numberofdays', 'number', array('label' => 'aantal dagen')) ->add('price', 'number', array('label' => 'prijs')) ->add('pricekmo', 'number', array('label' => 'kmo-portefeuille prijs')) ->add('location', 'sonata_type_model', array('expanded' => true, 'by_reference' => false, 'multiple' => true, 'btn_add' => false)) ->add('session', 'sonata_type_collection', array( 'by_reference' => false, 'type_options' => array( // prevents "delete" option being displayed 'delete' => false, 'delete_options' => array( // may otherwise choose put field hide 'type' => 'hidden', // in case, need fill in options 'type_options' => array( 'mapped' => false, 'required' => false, ) ) ) ), array( 'edit' => 'inline', 'inline' => 'table', 'sortable' => 'position' )) ; }
in sessionadmin class have:
protected function configureformfields(formmapper $formmapper) { $formmapper ->add('type', 'text', array('label' => 'type opleiding (dag / avond)')) ->add('moment', 'sonata_type_collection', array( 'by_reference' => false, 'type_options' => array( // prevents "delete" option being displayed 'delete' => false, 'delete_options' => array( // may otherwise choose put field hide 'type' => 'hidden', // in case, need fill in options 'type_options' => array( 'mapped' => false, 'required' => false, ) ) ) ), array( 'edit' => 'inline', 'inline' => 'table', 'sortable' => 'position' )) ; }
and in momentadmin class have:
protected function configureformfields(formmapper $formmapper) { $formmapper ->add('time', 'date', array('label' => 'datum')) ; }
the problem in form when try add moment (date) session following error:
fatalerrorexception: error: call member function getname() on null in /myproject/app/cache/dev/classes.php line 9772
so, can add session when try add moment session i'm getting error ... .
when in file classes.php on rule 9771 , 9772 there is:
$childformbuilder = $this->getchildformbuilder($formbuilder, $elementid); $fielddescription = $admin->getformfielddescription($childformbuilder->getname());
the $childformbuilder
null.
when @ function this:
public function getchildformbuilder(formbuilder $formbuilder, $elementid) { foreach (new formbuilderiterator($formbuilder) $name => $formbuilder) { if ($name == $elementid) { return $formbuilder; } } return; }
when var_dump of $name , $elementid this:
public function getchildformbuilder(formbuilder $formbuilder, $elementid) { foreach (new formbuilderiterator($formbuilder) $name => $formbuilder) { var_dump("name: " . $name); var_dump("elementid: " . $elementid); if ($name == $elementid) { return $formbuilder; } } die; return; }
and push on add new button following picture:
then output:
name: s56cda71d2daa0_name elementid: s56cda71d2daa0_session_0_moment name: s56cda71d2daa0_description elementid: s56cda71d2daa0_session_0_moment name: s56cda71d2daa0_materials elementid: s56cda71d2daa0_session_0_moment name: s56cda71d2daa0_numberofparticipants elementid: s56cda71d2daa0_session_0_moment name: s56cda71d2daa0_numberofdays elementid: s56cda71d2daa0_session_0_moment name: s56cda71d2daa0_price elementid: s56cda71d2daa0_session_0_moment name: s56cda71d2daa0_pricekmo elementid: s56cda71d2daa0_session_0_moment name: s56cda71d2daa0_location elementid: s56cda71d2daa0_session_0_moment name: s56cda71d2daa0_session elementid: s56cda71d2daa0_session_0_moment
in entities have __tostring function. example in course entity:
public function __tostring() { if(!is_null($this->name)) { return $this->name; } else{ return ""; } }
what problem here? i'm stuck this. i've posted issue on github repo of sonata admin no answers ...
my entities:
course entity:
<?php namespace studyx\enrolmentbundle\entity; use doctrine\orm\mapping orm; /** * course * * @orm\table(name="course") * @orm\entity */ class course { /** * @var string * * @orm\column(name="name", type="string", length=255, nullable=false) */ private $name; /** * @var string * * @orm\column(name="description", type="text", nullable=false) */ private $description; /** * @var string * * @orm\column(name="materials", type="text", nullable=true) */ private $materials; /** * @var integer * * @orm\column(name="number_of_participants", type="integer", nullable=true) */ private $numberofparticipants; /** * @var integer * * @orm\column(name="number_of_days", type="integer", nullable=true) */ private $numberofdays; /** * @var string * * @orm\column(name="price", type="decimal", nullable=true) */ private $price; /** * @var string * * @orm\column(name="price_kmo", type="decimal", nullable=true) */ private $pricekmo; /** * @var integer * * @orm\column(name="id", type="integer") * @orm\id * @orm\generatedvalue(strategy="identity") */ private $id; /** * @var \doctrine\common\collections\collection * * @orm\manytomany(targetentity="studyx\enrolmentbundle\entity\location", inversedby="course") * @orm\jointable(name="course_has_location", * joincolumns={ * @orm\joincolumn(name="course_id", referencedcolumnname="id") * }, * inversejoincolumns={ * @orm\joincolumn(name="location_id", referencedcolumnname="id") * } * ) */ private $location; /** * @var \doctrine\common\collections\collection * * @orm\onetomany(targetentity="studyx\enrolmentbundle\entity\session", mappedby="course") */ private $session; /** * add session * * @param \studyx\enrolmentbundle\entity\session $session * @return session */ public function addsession(\studyx\enrolmentbundle\entity\session $session) { $this->session[] = $session; return $this; } /** * remove session * * @param \studyx\enrolmentbundle\entity\session $session */ public function removesession(\studyx\enrolmentbundle\entity\session $session) { $this->session->removeelement($session); } /** * session * * @return \doctrine\common\collections\collection */ public function getsession() { return $this->session; } /** * constructor */ public function __construct() { $this->location = new \doctrine\common\collections\arraycollection(); } public function __tostring() { if(!is_null($this->name)) { return $this->name; } else{ return ""; } } /** * set name * * @param string $name * @return course */ public function setname($name) { $this->name = $name; return $this; } /** * name * * @return string */ public function getname() { return $this->name; } /** * set description * * @param string $description * @return course */ public function setdescription($description) { $this->description = $description; return $this; } /** * description * * @return string */ public function getdescription() { return $this->description; } /** * set materials * * @param string $materials * @return course */ public function setmaterials($materials) { $this->materials = $materials; return $this; } /** * materials * * @return string */ public function getmaterials() { return $this->materials; } /** * set numberofparticipants * * @param integer $numberofparticipants * @return course */ public function setnumberofparticipants($numberofparticipants) { $this->numberofparticipants = $numberofparticipants; return $this; } /** * numberofparticipants * * @return integer */ public function getnumberofparticipants() { return $this->numberofparticipants; } /** * set numberofdays * * @param integer $numberofdays * @return course */ public function setnumberofdays($numberofdays) { $this->numberofdays = $numberofdays; return $this; } /** * numberofdays * * @return integer */ public function getnumberofdays() { return $this->numberofdays; } /** * set price * * @param string $price * @return course */ public function setprice($price) { $this->price = $price; return $this; } /** * price * * @return string */ public function getprice() { return $this->price; } /** * set pricekmo * * @param string $pricekmo * @return course */ public function setpricekmo($pricekmo) { $this->pricekmo = $pricekmo; return $this; } /** * pricekmo * * @return string */ public function getpricekmo() { return $this->pricekmo; } /** * id * * @return integer */ public function getid() { return $this->id; } /** * add location * * @param \studyx\enrolmentbundle\entity\location $location * @return course */ public function addlocation(\studyx\enrolmentbundle\entity\location $location) { $this->location[] = $location; return $this; } /** * remove location * * @param \studyx\enrolmentbundle\entity\location $location */ public function removelocation(\studyx\enrolmentbundle\entity\location $location) { $this->location->removeelement($location); } /** * location * * @return \doctrine\common\collections\collection */ public function getlocation() { return $this->location; } }
session entity:
<?php namespace studyx\enrolmentbundle\entity; use doctrine\orm\mapping orm; /** * session * * @orm\table(name="session") * @orm\entity */ class session { /** * @var string * * @orm\column(name="type", type="string", length=45, nullable=false) */ private $type; /** * @var integer * * @orm\column(name="id", type="integer") * @orm\id * @orm\generatedvalue(strategy="identity") */ private $id; /** * @var \studyx\enrolmentbundle\entity\course * * @orm\manytoone(targetentity="studyx\enrolmentbundle\entity\course", inversedby="session") * @orm\joincolumns({ * @orm\joincolumn(name="course_id", referencedcolumnname="id") * }) */ private $course; /** * @var \doctrine\common\collections\collection * * @orm\onetomany(targetentity="studyx\enrolmentbundle\entity\moment", mappedby="session") */ private $moment; /** * add moment * * @param \studyx\enrolmentbundle\entity\moment $moment * @return moment */ public function addmoment(\studyx\enrolmentbundle\entity\moment $moment) { $this->moment[] = $moment; return $this; } /** * remove moment * * @param \studyx\enrolmentbundle\entity\moment $moment */ public function removemoment(\studyx\enrolmentbundle\entity\moment $moment) { $this->moment->removeelement($moment); } /** * moment * * @return \doctrine\common\collections\collection */ public function getmoment() { return $this->moment; } public function __tostring() { if(!is_null($this->type)) { return $this->type; } else{ return ""; } } /** * set type * * @param string $type * @return session */ public function settype($type) { $this->type = $type; return $this; } /** * type * * @return string */ public function gettype() { return $this->type; } /** * id * * @return integer */ public function getid() { return $this->id; } /** * set course * * @param \studyx\enrolmentbundle\entity\course $course * @return session */ public function setcourse(\studyx\enrolmentbundle\entity\course $course = null) { $this->course = $course; return $this; } /** * course * * @return \studyx\enrolmentbundle\entity\course */ public function getcourse() { return $this->course; } }
moment entity:
<?php namespace studyx\enrolmentbundle\entity; use doctrine\orm\mapping orm; /** * moment * * @orm\table(name="moment") * @orm\entity */ class moment { /** * @var \datetime * * @orm\column(name="time", type="datetime", nullable=false) */ private $time; /** * @var integer * * @orm\column(name="id", type="integer") * @orm\id * @orm\generatedvalue(strategy="identity") */ private $id; /** * @var \studyx\enrolmentbundle\entity\session * * @orm\manytoone(targetentity="studyx\enrolmentbundle\entity\session") * @orm\joincolumns({ * @orm\joincolumn(name="session_id", referencedcolumnname="id") * }) */ private $session; public function __tostring() { if(!is_null($this->time)) { return $this->time; } else{ return ""; } } /** * set time * * @param \datetime $time * @return moment */ public function settime($time) { $this->time = $time; return $this; } /** * time * * @return \datetime */ public function gettime() { return $this->time; } /** * id * * @return integer */ public function getid() { return $this->id; } /** * set session * * @param \studyx\enrolmentbundle\entity\session $session * @return moment */ public function setsession(\studyx\enrolmentbundle\entity\session $session = null) { $this->session = $session; return $this; } /** * session * * @return \studyx\enrolmentbundle\entity\session */ public function getsession() { return $this->session; } }
update:
i've added var_dumps function getchildformbuilder this:
public function getchildformbuilder(formbuilder $formbuilder, $elementid) { foreach (new formbuilderiterator($formbuilder) $name => $formbuilder) { if ($name == $elementid) { return $formbuilder; } } var_dump(__method__); var_dump($elementid); var_dump(debug_backtrace()); return; }
the result this:
string 'sonata\adminbundle\admin\adminhelper::getchildformbuilder' (length=57) string 's56cdfa72c4dea_session_0_moment' (length=31) array (size=8) 0 => array (size=7) 'file' => string '/applications/mamp/htdocs/studyx_enrolments/app/cache/dev/classes.php' (length=69) 'line' => int 9774 'function' => string 'getchildformbuilder' (length=19) 'class' => string 'sonata\adminbundle\admin\adminhelper' (length=36) 'object' => object(sonata\adminbundle\admin\adminhelper)[339] protected 'pool' => object(sonata\adminbundle\admin\pool)[104] ... 'type' => string '->' (length=2) 'args' => array (size=2) 0 => object(symfony\component\form\formbuilder)[436] ... 1 => &string 's56cdfa72c4dea_session_0_moment' (length=31) 1 => array (size=7) 'file' => string '/applications/mamp/htdocs/studyx_enrolments/vendor/sonata-project/admin-bundle/controller/helpercontroller.php' (length=110) 'line' => int 95 'function' => string 'appendformfieldelement' (length=22) 'class' => string 'sonata\adminbundle\admin\adminhelper' (length=36) 'object' => object(sonata\adminbundle\admin\adminhelper)[339] protected 'pool' => object(sonata\adminbundle\admin\pool)[104] ... 'type' => string '->' (length=2) 'args' => array (size=3) 0 => object(studyx\enrolmentbundle\admin\courseadmin)[370] ... 1 => object(studyx\enrolmentbundle\entity\course)[415] ... 2 => &string 's56cdfa72c4dea_session_0_moment' (length=31) 2 => array (size=5) 'function' => string 'appendformfieldelementaction' (length=28) 'class' => string 'sonata\adminbundle\controller\helpercontroller' (length=46) 'object' => object(sonata\adminbundle\controller\helpercontroller)[244] protected 'twig' => object(twig_environment)[220] ... protected 'helper' => object(sonata\adminbundle\admin\adminhelper)[339] ... protected 'pool' => object(sonata\adminbundle\admin\pool)[104] ... protected 'validator' => object(symfony\component\validator\validator)[340] ... 'type' => string '->' (length=2) 'args' => array (size=1) 0 => object(symfony\component\httpfoundation\request)[6] ... 3 => array (size=4) 'file' => string '/applications/mamp/htdocs/studyx_enrolments/app/bootstrap.php.cache' (length=67) 'line' => int 2957 'function' => string 'call_user_func_array' (length=20) 'args' => array (size=2) 0 => & array (size=2) ... 1 => & array (size=1) ... 4 => array (size=7) 'file' => string '/applications/mamp/htdocs/studyx_enrolments/app/bootstrap.php.cache' (length=67) 'line' => int 2931 'function' => string 'handleraw' (length=9) 'class' => string 'symfony\component\httpkernel\httpkernel' (length=39) 'object' => object(symfony\component\httpkernel\dependencyinjection\containerawarehttpkernel)[300] protected 'container' => object(appdevdebugprojectcontainer)[304] ... protected 'dispatcher' => object(symfony\component\httpkernel\debug\traceableeventdispatcher)[299] ... protected 'resolver' => object(symfony\component\httpkernel\controller\traceablecontrollerresolver)[249] ... 'type' => string '->' (length=2) 'args' => array (size=2) 0 => object(symfony\component\httpfoundation\request)[6] ... 1 => &int 1 5 => array (size=7) 'file' => string '/applications/mamp/htdocs/studyx_enrolments/app/bootstrap.php.cache' (length=67) 'line' => int 3060 'function' => string 'handle' (length=6) 'class' => string 'symfony\component\httpkernel\httpkernel' (length=39) 'object' => object(symfony\component\httpkernel\dependencyinjection\containerawarehttpkernel)[300] protected 'container' => object(appdevdebugprojectcontainer)[304] ... protected 'dispatcher' => object(symfony\component\httpkernel\debug\traceableeventdispatcher)[299] ... protected 'resolver' => object(symfony\component\httpkernel\controller\traceablecontrollerresolver)[249] ... 'type' => string '->' (length=2) 'args' => array (size=3) 0 => object(symfony\component\httpfoundation\request)[6] ... 1 => &int 1 2 => &boolean true 6 => array (size=7) 'file' => string '/applications/mamp/htdocs/studyx_enrolments/app/bootstrap.php.cache' (length=67) 'line' => int 2333 'function' => string 'handle' (length=6) 'class' => string 'symfony\component\httpkernel\dependencyinjection\containerawarehttpkernel' (length=73) 'object' => object(symfony\component\httpkernel\dependencyinjection\containerawarehttpkernel)[300] protected 'container' => object(appdevdebugprojectcontainer)[304] ... protected 'dispatcher' => object(symfony\component\httpkernel\debug\traceableeventdispatcher)[299] ... protected 'resolver' => object(symfony\component\httpkernel\controller\traceablecontrollerresolver)[249] ... 'type' => string '->' (length=2) 'args' => array (size=3) 0 => object(symfony\component\httpfoundation\request)[6] ... 1 => &int 1 2 => &boolean true 7 => array (size=7) 'file' => string '/applications/mamp/htdocs/studyx_enrolments/web/app_dev.php' (length=59) 'line' => int 29 'function' => string 'handle' (length=6) 'class' => string 'symfony\component\httpkernel\kernel' (length=35) 'object' => object(appkernel)[5] protected 'bundles' => array (size=22) ... protected 'bundlemap' => array (size=22) ... protected 'container' => object(appdevdebugprojectcontainer)[304] ... protected 'rootdir' => string '/applications/mamp/htdocs/studyx_enrolments/app' (length=47) protected 'environment' => string 'dev' (length=3) protected 'debug' => boolean true protected 'booted' => boolean true protected 'name' => string 'app' (length=3) protected 'starttime' => float 1456339594.61 protected 'loadclasscache' => array (size=2) ... 'type' => string '->' (length=2) 'args' => array (size=1) 0 => object(symfony\component\httpfoundation\request)[6] ...
update 2:
i've changed require in composer.json "sonata-project/admin-bundle": "^2.4@dev" , updated composer. i'm getting error:
contexterrorexception: warning: illegal string offset 'admin' in app/cache/dev/classes.php line 10482
the error in function:
public function getdashboardgroups() { $groups = $this->admingroups; foreach ($this->admingroups $name => $admingroup) { if (isset($admingroup['items'])) { foreach ($admingroup['items'] $key => $item) { if (''!= $item['admin']) { $admin = $this->getinstance($item['admin']); if ($admin->showin(admin::context_dashboard)) { $groups[$name]['items'][$key] = $admin; } else { unset($groups[$name]['items'][$key]); } } else { unset($groups[$name]['items'][$key]); } } } if (empty($groups[$name]['items'])) { unset($groups[$name]); } } return $groups; }
the errors in on line : if (''!= $item['admin']) {
.
in config.yml have:
sonata_admin: title: studyx title_logo: bundles/studyxenrolment/images/logo.png templates: layout: studyxenrolmentbundle:admin:standard_layout.html.twig edit: studyxenrolmentbundle:crud:edit.html.twig user_block: studyxenrolmentbundle:admin:user_block.html.twig # search: sonataadminbundle:core:search.html.twig # search_result_block: sonataadminbundle:block:block_search_result.html.twig dashboard: groups: studyx.admin.group.inschrijvingen: label: inschrijvingen items: ~ item_adds: - sonata.admin.enrolment studyx.admin.group.algemeen: label: algemeen items: ~ item_adds: - sonata.admin.course - sonata.admin.student studyx.admin.group.extra: label: items: ~ item_adds: - sonata.admin.location blocks: - position: top class: col-md-12 type: sonata.admin.block.admin_list
so think function getdashboardgroups called there.
update 3:
in composer.json have following:
"sonata-project/block-bundle": "~2.3", "sonata-project/admin-bundle": "^2.4@dev", "sonata-project/doctrine-orm-admin-bundle": "2.3.*", "sonata-project/formatter-bundle": "^2.3"
should update them ^2.4@dev
?
this error occurs because have more 2 level of nested collection forms, , it's not yet supported in release of sonata-admin.
from @rande (owner) , sonata maintainers @ issues #262, #1228, #1327 , #1971 :
this still not supported ....
you can @ old pr #1971 should solve problem use cases only.
the solution propose implement fix provided last opened pr #2985.
because pr not merged, need tell composer load rather current (not-working expected) version. (see composer , vcs).
hope pr merged soon.
until is, feel free fix problem @ moment using directly, several people do.
update
the pull request #3553 has been merged , fix problem of nested collections @ > 2 levels (nested in nested).
to fixed release, must use dev-master
tag of bundle (at least commit 926f159 representing merge of pr).
i tried , works following requirement:
// composer.json "require": { "sonata-project/admin-bundle": "^2.4@dev", ... },
i hope can upgrade bundle in order fix.
update2
apparently, composer doesn't take last changes of branch.
fix provided pr #2739 has been merged 6 days ago.
to fix last 1 (hope), need change short code block in adddepencycallscompilerpass
located in vendor/sonata-project/admin-bundle/dependencyinjection/compiler/adddependencycallscompilerpass
.
at line 95, replace line :
$groupdefaults[$resolvedgroupname]['items'][] = $id;
to ones:
$groupdefaults[$resolvedgroupname]['items'][] = array( 'admin' => $id, 'label' => !empty($attributes['label']) ? $attributes['label'] : '', 'route' => '', 'route_params' => array(), );
like it's done pr (it's needed change make working).
i make fix manually because it's new, , after days/weeks, run following commands:
composer clear-cache
, composer update sonata-project/admin-bundle
you should try before adding fix manually, maybe changes added.
also, can use link composer , vcs given @ begin of answer , require fix directly. it's @ own appreciation because of it's solution @ moment.
and last, patient, fixes merged in stable releases soon.
Comments
Post a Comment