Phalcon php orm can't relate tables - php

I'm trying to setup a 3 tables relationship in phalcon, but it doesn't seem to work. Could anyone plese tell me what am I doing wrong? Here is a ERD of these tables: (ERD diagram) Here is my code from models and controller
Authors.php (Model)
<?php
class Authors extends \Phalcon\Mvc\Model
{
/**
*
* #var integer
*/
public $ID;
/**
*
* #var string
*/
public $Name;
/**
*
* #var string
*/
public $LastName;
/**
* Initialize method for model.
*/
public function initialize()
{
$this->setSource('Authors');
$this->hasMany('ID', 'Authorbook', 'AuthorID');
}
/**
* Independent Column Mapping.
*/
public function columnMap()
{
return array(
'ID' => 'ID',
'Name' => 'Name',
'LastName' => 'LastName'
);
}
Books.php(Model)
<?php
class Books extends \Phalcon\Mvc\Model
{
/**
*
* #var integer
*/
public $ID;
/**
*
* #var string
*/
public $Name;
/**
*
* #var string
*/
public $YearPublished;
/**
*
* #var string
*/
public $Picture;
/**
* Initialize method for model.
*/
public function initialize()
{
$this->setSource('Books');
$this->hasMany('ID', 'Authorbook', 'BookID');
}
/**
* Independent Column Mapping.
*/
public function columnMap()
{
return array(
'ID' => 'ID',
'Name' => 'Name',
'YearPublished' => 'YearPublished',
'Picture' => 'Picture'
);
}
}
}
AuthorBook.php (Model):
<?php
class Authorbook extends \Phalcon\Mvc\Model
{
/**
*
* #var integer
*/
public $ID;
/**
*
* #var integer
*/
public $AuthorID;
/**
*
* #var integer
*/
public $BookID;
/**
* Initialize method for model.
*/
public function initialize()
{
$this->setSource('AuthorBook');
$this->belongsTo('AuthorID', 'Authors', 'ID');
$this->belongsTo('BookID', 'Books', 'ID');
}
/**
* Independent Column Mapping.
*/
public function columnMap()
{
return array(
'ID' => 'ID',
'AuthorID' => 'AuthorID',
'BookID' => 'BookID'
);
}
}
AdminController.php
public function indexAction()
{
$this->view->disableLevel(View::LEVEL_MAIN_LAYOUT);
$this->view->setVar('books', Books::find()->toArray());
}
}
So my question is how can I get access to the author of a book? Because when I print out my books array in view only thing I get is :
Array
(
[0] => Array
(
[ID] => 1
[Name] => Javascript: The Good Parts
[YearPublished] => 2014-04-18
[Picture] => javascript-the-good-parts.jpg
)
...

You should use the hasManyToMany for n-n relationship for the Authors and Books models, (if I'm not wrong, Author has many Books and Books has many Authors)
so you models should be like this:
for Authors:
public function initialize(){
$this->setSource('Authors');
$this->hasManyToMany('ID', 'Authorbook', 'AuthorID', 'BookID', 'Books', 'ID', array(
'alias' => 'books'
));
}
same for the Books model, we have to use the hasManyToMany function:
public function initialize(){
$this->setSource('Books');
$this->hasManyToMany('ID', 'Authorbook', 'BookID', 'AuthorID', 'Author', 'ID', array(
'alias' => 'authors'
));
}
the AuthorBook model relations are correct.
Now you can get the author(s) of a book with a simple call to the alias that we have defined in the relations:
$book = Books::findFirst();
$bookAuthor = $book->authors->toArray()
that's it.

I copied an example code on Phalcon site to test model relationship and ran into similar issue. I need to specific the namespace when set relationship. For example, in function initialize of Robots class
public function initialize()
{
$this->getSource('robots');
$this->hasMany('id', 'SomeNameSpace\RobotsParts', 'robots_id',array(
'alias' => 'robotsparts',
));
}

Related

Doctrine persisting Many-To-One entities

I'm using Zend Framework 3 with Doctrine and I'm trying to save an Entity "Cidade" related to another Entity "Estado" witch is already stored in the database. However, Doctrine is trying to persist Entity "Estado", and the only attribute I have from Estado is the primary key in a HTML combo.
My view forms are built under Zend forms and fieldsets, which means POST data is automatically converted to the target entities using ClassMethods hydrator.
The problem is that if I set the attribute $estado with cascade={"persist"} in Cidade Entity, Doctrine tries to persist Estado Entity missing all required attributes but the primary key ID, which comes from POST request (HTML combo). I also considered using cascade={"detach"} ir order to Doctrine ignore Estado Entity in the EntityManager. But I get this error:
A new entity was found through the relationship 'Application\Entity\Cidade#estado' that was not configured to cascade persist operations for entity: Application\Entity\Estado#000000007598ee720000000027904e61.
I found a similar doubt here and the only way I could find for this matter was first retrieving Estado Entity and setting it on Cidade Entity before saving. If this is the only way, can I tell my form structure won't work unless I retrieve all relationships before saving the dependant entities?
In other words, what is the best way of doing such thing in Doctrine (for example):
<?php
/*I'm simulating the creation of Estado Entity representing an
existing Estado in database, so "3" is the ID rendered in HTML combo*/
$estado = new Entity\Estado();
$estado->setId(3);
$cidade = new Entity\Cidade();
$cidade->setNome("City Test");
$cidade->setEstado($estado); //relationship here
$entityManager->persist($cidade);
$entityManager->flush();
How to do that without having to retrieve an Estado all the time I need to save a Cidade? Wouldn't affect performance?
My Cidade Entity:
<?php
namespace Application\Entity;
use Zend\InputFilter\Factory;
use Zend\InputFilter\InputFilterInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* Class Cidade
* #package Application\Entity
* #ORM\Entity
*/
class Cidade extends AbstractEntity
{
/**
* #var string
* #ORM\Column(length=50)
*/
private $nome;
/**
* #var Estado
* #ORM\ManyToOne(targetEntity="Estado", cascade={"detach"})
* #ORM\JoinColumn(name="id_estado", referencedColumnName="id")
*/
private $estado;
/**
* Retrieve input filter
*
* #return InputFilterInterface
*/
public function getInputFilter()
{
if (!$this->inputFilter) {
$factory = new Factory();
$this->inputFilter = $factory->createInputFilter([
"nome" => ["required" => true]
]);
}
return $this->inputFilter;
}
/**
* #return string
*/
public function getNome()
{
return $this->nome;
}
/**
* #param string $nome
*/
public function setNome($nome)
{
$this->nome = $nome;
}
/**
* #return Estado
*/
public function getEstado()
{
return $this->estado;
}
/**
* #param Estado $estado
*/
public function setEstado($estado)
{
$this->estado = $estado;
}
}
My Estado Entity:
<?php
namespace Application\Entity;
use Doctrine\ORM\Mapping as ORM;
use Zend\InputFilter\Factory;
use Zend\InputFilter\InputFilterInterface;
/**
* Class Estado
* #package Application\Entity
* #ORM\Entity
*/
class Estado extends AbstractEntity
{
/**
* #var string
* #ORM\Column(length=50)
*/
private $nome;
/**
* #var string
* #ORM\Column(length=3)
*/
private $sigla;
/**
* #return string
*/
public function getNome()
{
return $this->nome;
}
/**
* #param string $nome
*/
public function setNome($nome)
{
$this->nome = $nome;
}
/**
* #return string
*/
public function getSigla()
{
return $this->sigla;
}
/**
* #param string $sigla
*/
public function setSigla($sigla)
{
$this->sigla = $sigla;
}
/**
* Retrieve input filter
*
* #return InputFilterInterface
*/
public function getInputFilter()
{
if (!$this->inputFilter) {
$factory = new Factory();
$this->inputFilter = $factory->createInputFilter([
"nome" => ["required" => true],
"sigla" => ["required" => true]
]);
}
return $this->inputFilter;
}
}
Both entities extend my superclass AbstractEntity:
<?php
namespace Application\Entity;
use Doctrine\ORM\Mapping\MappedSuperclass;
use Doctrine\ORM\Mapping as ORM;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
/**
* Class AbstractEntity
* #package Application\Entity
* #MappedSuperClass
*/
abstract class AbstractEntity implements InputFilterAwareInterface
{
/**
* #var int
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #var InputFilterAwareInterface
*/
protected $inputFilter;
/**
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* #param int $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* #param InputFilterInterface $inputFilter
* #return InputFilterAwareInterface
* #throws \Exception
*/
public function setInputFilter(InputFilterInterface $inputFilter)
{
throw new \Exception("Método não utilizado");
}
}
My HTML inputs are rendered as it follows:
<input name="cidade[nome]" class="form-control" value="" type="text">
<select name="cidade[estado][id]" class="form-control">
<option value="3">Bahia</option>
<option value="2">Espírito Santo</option>
<option value="1">Minas Gerais</option>
<option value="9">Pará</option>
</select>
Each option above is an Estado Entity retrieved from database. My POST data comes as the following example:
[
"cidade" => [
"nome" => "Test",
"estado" => [
"id" => 3
]
]
]
On Zend Form's isValid() method, this POST data is automatically converted to the target Entities, which makes me crash on this Doctrine issue. How do I move on?
You should bind an object to your form and use the Doctrine Hydrator. In the form the field names should exactly match that of the Entity. So Entity#name is Form#name.
With Separation of Concerns I'm absolutely against placing the InputFilter for an Entity within the Entity itself. As such, I'll give you an example with everything separated, if you decide to mash it back together, that's up to you.
AbstractEntity for ID
/**
* #ORM\MappedSuperclass
*/
abstract class AbstractEntity
{
/**
* #var int
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
// getter/setter
}
Cicade Entity
/**
* #ORM\Entity
*/
class Cidade extends AbstractEntity
{
/**
* #var string
* #ORM\Column(length=50)
*/
protected $nome; // Changed to 'protected' so can be used in child classes - if any
/**
* #var Estado
* #ORM\ManyToOne(targetEntity="Estado", cascade={"persist", "detach"}) // persist added
* #ORM\JoinColumn(name="id_estado", referencedColumnName="id")
*/
protected $estado;
// getters/setters
}
Estado Entity
/**
* #ORM\Entity
*/
class Estado extends AbstractEntity
{
/**
* #var string
* #ORM\Column(length=50)
*/
protected $nome;
//getters/setters
}
So, above is the Entity setup for Many to One - Uni-direction relation.
You want to manage this, easily, with forms. So we need to create InputFilters for both.
Having InputFilters separately from the Entity allows us to nest them. This in turn allows us to create structured and nested forms.
For example, you could create a new Estado on-the-fly. If this were a bi-directional relation, you could create multiple Cicade Entity objects on-the-fly from/during the creation of Estado.
First: InputFilters. In the spirit of abstraction, which you started with your Entities, let's do that here as well:
AbstractDoctrineInputFilter
source AbstractDoctrineInputFilter & source AbstractDoctrineFormInputFilter
This gives a nice clean setup and a requirement to fulfill. I'm glossing over the more complex elements added in the source files, feel free to look those up though.
Both objects (Estado & Cicade) require an ObjectManager (they're Doctrine entities after all), so I'm assuming you might have more. The below should come in handy.
<?php
namespace Application\InputFilter;
use Doctrine\Common\Persistence\ObjectManager;
use Zend\InputFilter\InputFilter;
abstract class AbstractInputFilter extends InputFilter
{
/**
* #var ObjectManager
*/
protected $objectManager;
/**
* AbstractFormInputFilter constructor.
*
* #param array $options
*/
public function __construct(array $options)
{
// Check if ObjectManager|EntityManager for FormInputFilter is set
if (isset($options['object_manager']) && $options['object_manager'] instanceof ObjectManager) {
$this->setObjectManager($options['object_manager']);
}
}
/**
* Init function
*/
public function init()
{
$this->add(
[
'name' => 'id',
'required' => false, // Not required when adding - should also be in route when editing and bound in controller, so just additional
'filters' => [
['name' => ToInt::class],
],
'validators' => [
['name' => IsInt::class],
],
]
);
// If CSRF validation has not been added, add it here
if ( ! $this->has('csrf')) {
$this->add(
[
'name' => 'csrf',
'required' => true,
'filters' => [],
'validators' => [
['name' => Csrf::class],
],
]
);
}
}
// getters/setters for ObjectManager
}
Estado InputFilter
class EstadoInputFilter extends AbstractInputFilter
{
public function init()
{
parent::init();
$this->add(
[
'name' => 'nome', // <-- important, name matches entity property
'required' => true,
'allow_empty' => true,
'filters' => [
['name' => StringTrim::class],
['name' => StripTags::class],
[
'name' => ToNull::class,
'options' => [
'type' => ToNull::TYPE_STRING,
],
],
],
'validators' => [
[
'name' => StringLength::class,
'options' => [
'min' => 2,
'max' => 255,
],
],
],
]
);
}
}
Cicade InputFilter
class EstadoInputFilter extends AbstractInputFilter
{
public function init()
{
parent::init(); // Adds the CSRF
$this->add(
[
'name' => 'nome', // <-- important, name matches entity property
'required' => true,
'allow_empty' => true,
'filters' => [
['name' => StringTrim::class],
['name' => StripTags::class],
[
'name' => ToNull::class,
'options' => [
'type' => ToNull::TYPE_STRING,
],
],
],
'validators' => [
[
'name' => StringLength::class,
'options' => [
'min' => 2,
'max' => 255,
],
],
],
]
);
$this->add(
[
'name' => 'estado',
'required' => true,
]
);
}
}
So. Now we have 2 InputFilters, based on an AbstractInputFilter.
EstadoInputFilter filters just the nome property. Add additional if you want ;)
CicadeInputFilter filters the nome property and has a required estado field.
The names match those of the Entity definition in the respective Entity classes.
Just to be complete, below is the CicadeForm, take what you need to create the EstadoForm.
class CicadeForm extends Form
{
/**
* #var ObjectManager
*/
protected $objectManager;
/**
* AbstractFieldset constructor.
*
* #param ObjectManager $objectManager
* #param string $name Lower case short class name
* #param array $options
*/
public function __construct(ObjectManager $objectManager, string $name, array $options = [])
{
parent::__construct($name, $options);
$this->setObjectManager($objectManager);
}
public function init()
{
$this->add(
[
'name' => 'nome',
'required' => true,
'type' => Text::class,
'options' => [
'label' => _('Nome',
],
]
);
// #link: https://github.com/doctrine/DoctrineModule/blob/master/docs/form-element.md
$this->add(
[
'type' => ObjectSelect::class,
'required' => true,
'name' => 'estado',
'options' => [
'object_manager' => $this->getObjectManager(),
'target_class' => Estado::class,
'property' => 'id',
'display_empty_item' => true,
'empty_item_label' => '---',
'label' => _('Estado'),
'label_attributes' => [
'title' => _('Estado'),
],
'label_generator' => function ($targetEntity) {
/** #var Estado $targetEntity */
return $targetEntity->getNome();
},
],
]
);
//Call parent initializer. Check in parent what it does.
parent::init();
}
/**
* #return ObjectManager
*/
public function getObjectManager() : ObjectManager
{
return $this->objectManager;
}
/**
* #param ObjectManager $objectManager
*
* #return AbstractDoctrineFieldset
*/
public function setObjectManager(ObjectManager $objectManager) : AbstractDoctrineFieldset
{
$this->objectManager = $objectManager;
return $this;
}
}
Config
Now that the classes are there, how to use them? Slap 'em together with module config!
In your module.config.php file, add this config:
'form_elements' => [
'factories' => [
CicadeForm::class => CicadeFormFactory::class,
EstadoForm::class => EstadoFormFactory::class,
// If you create separate Fieldset classes, this is where you register those
],
],
'input_filters' => [
'factories' => [
CicadeInputFilter::class => CicadeInputFilterFactory::class,
EstadoInputFilter::class => EstadoInputFilterFactory::class,
// If you register Fieldsets in form_elements, their InputFilter counterparts go here
],
],
From this config we read we need a Factory for both the Form and for the InputFilter of a set.
Below the CicadeInputFilterFactory
class CicadeInputFilterFactory implements FactoryInterface
{
/**
* #param ContainerInterface $container
* #param string $requestedName
* #param array|null $options
*
* #return CicadeInputFilter
* #throws \Psr\Container\ContainerExceptionInterface
* #throws \Psr\Container\NotFoundExceptionInterface
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
/** #var ObjectManager|EntityManager $objectManager */
$objectManager = $this->setObjectManager($container->get(EntityManager::class));
return new CicadeInputFilter(
[
'object_manager' => objectManager,
]
);
}
}
Matching CicadeFormFactory
class CicadeFormFactory implements FactoryInterface
{
/**
* #param ContainerInterface $container
* #param string $requestedName
* #param array|null $options
*
* #return CicadeForm
* #throws \Psr\Container\ContainerExceptionInterface
* #throws \Psr\Container\NotFoundExceptionInterface
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null) : CicadeForm
{
$inputFilter = $container->get('InputFilterManager')->get(CicadeInputFilter::class);
// Here we creazte a new Form object. We set the InputFilter we created earlier and we set the DoctrineHydrator. This hydrator can work with Doctrine Entities and relations, so long as data is properly formatted when it comes in from front-end.
$form = $container->get(CicadeForm::class);
$form->setInputFilter($inputFilter);
$form->setHydrator(
new DoctrineObject($container->get(EntityManager::class))
);
$form->setObject(new Cicade());
return $form;
}
}
Massive preparation done, time to use it
Specific EditController to Edit an existing Cicade Entity
class EditController extends AbstractActionController // (Zend's AAC)
{
/**
* #var CicadeForm
*/
protected $cicadeForm;
/**
* #var ObjectManager|EntityManager
*/
protected $objectManager;
public function __construct(
ObjectManager $objectManager,
CicadeForm $cicadeForm
) {
$this->setObjectManager($objectManager);
$this->setCicadeForm($cicadeForm);
}
/**
* #return array|Response
* #throws ORMException|Exception
*/
public function editAction()
{
$id = $this->params()->fromRoute('id', null);
if (is_null($id)) {
$this->redirect()->toRoute('home'); // Do something more useful instead of this, like notify of id received from route
}
/** #var Cicade $entity */
$entity = $this->getObjectManager()->getRepository(Cicade::class)->find($id);
if (is_null($entity)) {
$this->redirect()->toRoute('home'); // Do something more useful instead of this, like notify of not found entity
}
/** #var CicadeForm $form */
$form = $this->getCicadeForm();
$form->bind($entity); // <-- This here is magic. Because we overwrite the object from the Factory with an existing one. This pre-populates the form with value and allows us to modify existing one. Assumes we got an entity above.
/** #var Request $request */
$request = $this->getRequest();
if ($request->isPost()) {
$form->setData($request->getPost());
if ($form->isValid()) {
/** #var Cicade $cicade */
$cicade = $form->getObject();
$this->getObjectManager()->persist($cicade);
try {
$this->getObjectManager()->flush();
} catch (Exception $e) {
throw new Exception('Could not save. Error was thrown, details: ', $e->getMessage());
}
$this->redirect()->toRoute('cicade/view', ['id' => $entity->getId()]);
}
}
return [
'form' => $form,
'validationMessages' => $form->getMessages() ?: '',
];
}
/**
* #return CicadeForm
*/
public function getCicadeForm() : CicadeForm
{
return $this->cicadeForm;
}
/**
* #param CicadeForm $cicadeForm
*
* #return EditController
*/
public function setCicadeForm(CicadeForm $cicadeForm) : EditController
{
$this->cicadeForm = $cicadeForm;
return $this;
}
/**
* #return ObjectManager|EntityManager
*/
public function getObjectManager() : ObjectManager
{
return $this->objectManager;
}
/**
* #param ObjectManager|EntityManager $objectManager
*
* #return EditController
*/
public function setObjectManager(ObjectManager $objectManager) : EditController
{
$this->objectManager = $objectManager;
return $this;
}
}
So, felt like giving a really expanded answer. Covers the whole thing really.
If you have any questions about the above, let me know ;-)

Fieldset validation data

I have problem with fieldsets in ZF2, I show you my problem.
Here is my form (made by AngularJS, not by ZF2), where you can put a name, and select if you permit which page or which action (Pages are composed of actions).
The picture below partially show what I send to ZF2 :
Here is my data model of my CustomRole class :
class CustomRole
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string")
*/
protected $name;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Users\CustomRolePagePermission", mappedBy="customRole")
*/
protected $pagePermissions;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Users\CustomRoleActionPermission", mappedBy="customRole")
*/
protected $actionPermissions;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Users\GlobalRole")
*/
protected $globalRole;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Cie", inversedBy="customRoles")
* #ORM\JoinColumn(name="cie", referencedColumnName="id_cie")
*/
protected $cie;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Users\User", mappedBy="customRole")
*/
protected $users;
...
Here my class CustomRolePagePermission (which is near the same than CustomRoleActionPermission) :
class CustomRolePagePermission extends PagePermission
{
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="App\Entity\Users\CustomRole", inversedBy="pagePermissions")
* #ORM\JoinColumn(name="custom_role_id", referencedColumnName="id", nullable=false)
*/
protected $customRole;
...
And then the abstract class PagePermission :
abstract class PagePermission
{
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="App\Entity\Page")
* #ORM\JoinColumn(name="page_id", referencedColumnName="id", nullable=false)
*/
protected $page;
/**
* #ORM\Column(type="boolean")
*/
protected $permission;
...
Now the Fieldset corresponding to CustomRole class (I have made it on each entities) :
class CustomRoleFieldset extends Fieldset implements InputFilterProviderInterface {
protected $serviceLocator;
public function __construct(EntityManager $entityManager) {
parent::__construct('role');
$this->setHydrator(new DoctrineHydrator($entityManager, 'App\Entity\Users\CustomRole'))
->setObject(new CustomRole());
$this->add(array('name' => 'name'));
$customRolePagePermissionFieldset = new CustomRolePagePermissionFieldset($entityManager);
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'pagePermission',
'options' => array(
'target_element' => $customRolePagePermissionFieldset
),
));
$customRoleActionPermissionFieldset = new CustomRoleActionPermissionFieldset($entityManager);
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'actionPermission',
'options' => array(
'target_element' => $customRoleActionPermissionFieldset
),
));
}
public function getInputFilterSpecification() {
return array(
'name' => array('required' => true),
'pagePermission' => array('required' => true),
'actionPermission' => array('required' => true),
);
}
}
...
Here my fieldset CustomRolePagePermissionFieldset :
class CustomRolePagePermissionFieldset extends Fieldset implements InputFilterProviderInterface {
protected $serviceManager;
public function __construct(EntityManager $entityManager) {
parent::__construct();
$this->setHydrator(new DoctrineHydrator($entityManager, 'App\Entity\Users\CustomRolePagePermission'))
->setObject(new CustomRolePagePermission());
$this->add(array('name' => 'permission'));
}
...
And then, my controller :
...
$customRoleForm = new CustomRoleForm($em);
$customRole = new CustomRole();
$formData = $request->getPost();
$customRoleForm->bind($customRole);
$customRoleForm->setData($formData);
if ($customRoleForm->isValid()) {
$customRole->setCie($cie);
$customRole->setGlobalRole($globalRole);
$em->persist($customRole);
$em->flush();
return $this->ok($customRole->getId());
}
...
Problem
When I send the form, the CustomRole is created, but pages and actions checked previously are not linked with the CustomRole created, as if I had never checked any checkbox.
I don't understand why it doesn't have effects, do you have any ideas ?
Thanks a lot in advance ! :)
Your collection should be "pagePermissions" for the hydrator to call setPagePermissions.

How to make symfony 2 auto select the drop down field in edit action

I have a category table and product table. The categories are listed in product form in a drop down select box. When i edit a product then category saved in product table should be auto selected in category drop down at product edit form (selected="selected").
I have already tried empty_data and empty_value but they does not work.
I am searching for the solution since 2 days but could not find any tutorial or such example. I will be really thankful if someone can refer me a tutorial or can see below code to sort out the problem:
Controller
<?php
// src/Scsp/CmsBundle/Controller/ProductController.php
namespace Scsp\CmsBundle\Controller;
use Scsp\CmsBundle\Entity\CategoryEntity;
use Scsp\CmsBundle\Entity\ProductEntity;
use Scsp\CmsBundle\Form\ProductForm;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\HttpFoundation\Session\Session;
class ProductController extends Controller {
public function editAction($id, $request) {
$em = $this->getDoctrine()->getManager();
$product = $em->getRepository('ScspCmsBundle:ProductEntity')->find($id);
if (!$product){
throw $this->createNotFoundException('No product found for id ' . $id);
}
$productForm=new ProductForm($id);
$form = $this->createForm($productForm, $product, array(
'attr' => array('class' => 'form-horizontal')
));
$form->handleRequest($request);
if ($request->isMethod('POST') && $form->isValid()) {
$product->setCategoryid(3);
$em->persist($product);
$em->flush();
$this->session->getFlashBag()->add('success', $product->getProducts() . ' successfully edited!');
return $this->redirect($this->generateUrl('scsp_cms_products', array('action' => 'list', 'id' => 0)));
}
return $this->render('ScspCmsBundle:Default:form.html.twig', array(
'form' => $form->createView(),
'page_title' => 'Edit product',
'type' => 'products',
'id' => $id
));
}
}
Product Entity:
<?php
// src/Scsp/CmsBundle/Entity/ProductEntity.php
namespace Scsp\CmsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="Scsp\CmsBundle\Repository\ProductRepository")
* #ORM\Table(name="products")
*/
class ProductEntity {
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=100)
*/
protected $products;
/**
* #ORM\Column(type="integer")
* #ORM\ManyToOne(targetEntity="CategoryEntity", inversedBy="products")
* #ORM\JoinColumn(name="categoryid", referencedColumnName="id")
*/
protected $categoryid;
/**
* Get id
*
* #return integer
*/
public function getId() {
return $this->id;
}
/**
* Set id
*
* #param string $id
* #return Id
*/
public function setId($id) {
$this->id = $id;
return $this;
}
/**
* Set products
*
* #param string $products
* #return ProductEntity
*/
public function setProducts($products)
{
$this->products = $products;
return $this;
}
/**
* Get products
*
* #return string
*/
public function getProducts()
{
return $this->products;
}
/**
* Set categoryid
*
* #param integer $categoryid
* #return ProductEntity
*/
public function setCategoryid($categoryid)
{
$this->categoryid = $categoryid;
return $this;
}
/**
* Get categoryid
*
* #return integer
*/
public function getCategoryid()
{
return $this->categoryid;
}
}
Category Entity:
<?php
// src/Scsp/CmsBundle/Entity/CategoryEntity.php
namespace Scsp\CmsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass="Scsp\CmsBundle\Repository\CategoryRepository")
* #ORM\Table(name="categories")
*/
class CategoryEntity {
/**
* #ORM\OneToMany(targetEntity="ProductEntity", mappedBy="categorid")
*/
protected $products;
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=100)
*/
protected $categories;
public function __construct(){
$this->products = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId() {
return $this->id;
}
/**
* Set id
*
* #param string $id
* #return Id
*/
public function setId($id) {
$this->id = $id;
return $this;
}
/**
* Set categories
*
* #param string $categories
* #return Categories
*/
public function setCategories($categories) {
$this->categories = $categories;
return $this;
}
/**
* Get categories
*
* #return string
*/
public function getCategories() {
return $this->categories;
}
public function __toString() {
return $this->getCategories();
}
/**
* Add products
*
* #param \Scsp\CmsBundle\Entity\ProductEntity $products
* #return CategoryEntity
*/
public function addProduct(\Scsp\CmsBundle\Entity\ProductEntity $products)
{
$this->products[] = $products;
return $this;
}
/**
* Remove products
*
* #param \Scsp\CmsBundle\Entity\ProductEntity $products
*/
public function removeProduct(\Scsp\CmsBundle\Entity\ProductEntity $products)
{
$this->products->removeElement($products);
}
/**
* Get products
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getProducts()
{
return $this->products;
}
}
Product Form:
<?php
// src/Scsp/CmsBundle/Form/ProductForm.php
namespace Scsp\CmsBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use \Scsp\CmsBundle\Entity\ProductEntity;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormInterface;
class ProductForm extends AbstractType
{
private $id;
public function __construct($id) {
$this->id=$id;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
if($this->id>0){
$builder->add('id', 'text',
array(
'label' => false,
'attr' => array(
'class' => 'form-control',
'readonly' => 'readonly'
)
)
);
}
$builder
->add('products', 'text',
array(
'label' => false,
'max_length' => 100,
'attr' => array(
'class' => 'form-control',
'autocomplete' => 'off'
)
)
)
->add('categoryid', 'entity', array(
'label' => false,
'class' => 'ScspCmsBundle:CategoryEntity',
'property' => 'categories',
'data'=>$builder->getData()->getCategoryId(),
'attr' => array(
)
)
)
->add('save', 'submit',array(
'attr' => array(
'formnovalidate' => 'formnovalidate',
'class' => 'btn btn-primary',
'label' => 'Save Changes'
)
)
)
->getForm();
}
public function getName()
{
return 'products';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'empty_data' => 'Scsp\CmsBundle\Entity\CategoryEntity'
));
}
}
categoryid
is not an entity, so you must to add a property called category in ProductEntity and stablish the relation ManyToOne there, the code:
/**
* #ORM\Column(type="integer")
* #ORM\ManyToOne(targetEntity="CategoryEntity", inversedBy="products")
* #ORM\JoinColumn(name="categoryid", referencedColumnName="id")
*/
protected $categoryid;
must be replaced by:
/**
* #ORM\Column(name="categoryid", type="integer")
*/
protected $categoryid;
/**
* #var CategoryEntity
* #ORM\ManyToOne(targetEntity="CategoryEntity", inversedBy="products")
* #ORM\JoinColumn(name="categoryid", referencedColumnName="id")
*/
protected $category;
//Here generate the get and the set customized
/**
* #return CategoryEntity
*/
public function getCategory()
{
return $this->category;
}
/**
* #param CategoryEntity $category
* #return $this
*/
public function setCategory($category)
{
//this is the owning side
//all changes will be detected
//in this side by doctrine
$category->addProduct($this);
$this->category= $category;
//The line below is important to set the categoryid
//field in the product table
$this->setCategoryid($category->getId());
return $this;
}
Now in the Product Form replace:
->add('categoryid', 'entity', array(
'label' => false,
'class' => 'ScspCmsBundle:CategoryEntity',
'property' => 'categories',
'data'=>$builder->getData()->getCategoryId(),
'attr' => array(
)
)
)
by:
->add('category', 'entity', array(
'label' => false,
'class' => 'ScspCmsBundle:CategoryEntity',
'property' => 'categories',
'attr' => array(
)
)
)
That's all, enjoy it!
I see many things wrong in your form type, nonetheless, what are you looking for is the data option in the field definition:
->add('categoryid', 'entity',
array(
'label' => false,
'class' => 'ScspCmsBundle:CategoryEntity',
'property' => 'categories',
'data' => $builder->getData()->getCategoryId()
)
)
This data field must contain the select value property, which is the categoryId, at least when using an entity field type as normal. I don't know if this will work with you current setup!
I have a solution for Symfony 1.4.18.
The key is use jQuery.
You can view a detail explanation (including images) in my blog (in Spanish) here: http://symfonyandme.com/2013/10/09/como-usar-select-dependientes-en-symfony-1-4-con-tres-tablas/
And the same solution here: Symfony 1.4 with AJAX jQuery. How can I improve the AJAX's function that contains a select box which depends on another select box?
Then you would work for adjust this example for Symfony 2

Handling multiple file uploads in Sonata Admin Bundle

So, after research a lot and get no results (maybe I'm a bad searcher) I coming from this topics: SonataAdmin Bundle File Upload Error and SonataMediaBundle - how to upload images? I can't find a solution for my problem. I have a Entity Company and each company can have multiple files: PDF, DOC, XLS and some other mime/types. I think to use VichUploaderBundle but again docs only covers example for one to one relationship so my question is, any can give me some examples or ways to get this done? I mean upload files and attach them to company?
EDIT1 working and testing
As I said before I'm trying to integrate SonataMediaBundle into another admin module I have but I can't get it to work. What I did until now?
Of course install and configure all bundles: SonataAdminBundle and SonataMediaBundle both are working fine
Modified \Application\Sonata\MediaBundle\Entity\Media.php class to add the needed functionality by adding a ManyToMany relationship
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", inversedBy="medias")
* #ORM\JoinTable(name="order_has_media__media",
* joinColumns={#ORM\JoinColumn(name="media__media_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="order_no_order", referencedColumnName="no_order")}
* )
*/
protected $orders;
public function __construct() {
$this->orders = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer $id
*/
public function getId() {
return $this->id;
}
public function setOrders(\PL\OrderBundle\Entity\Order $order) {
$this->orders[] = $order;
}
public function getOrders() {
return $this->orders;
}
}
Adding the need fields in PL\OrderBundle\Entity\Order.php as follow:
namespace PL\OrderBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="tb_order")
*/
class Order {
/**
* #ORM\Id
* #ORM\Column(type="string", length=15, unique=true, nullable=false)
*/
protected $no_order;
/**
* #ORM\ManyToOne(targetEntity="PL\CompanyBundle\Entity\Company", inversedBy="id")
*/
protected $company;
/**
* #ORM\Column(type="string", length=15, unique=true)
*/
protected $business_case;
/**
* #ORM\Column(type="integer", length=1)
*/
protected $charge_status;
/**
* #ORM\Column(type="datetime")
*/
protected $eta;
/**
* #ORM\Column(type="datetime")
*/
protected $etd;
/**
* #ORM\Column(type="integer", length=1)
*/
protected $transport_media;
/**
* #ORM\Column(type="integer", length=1)
*/
protected $incoterm;
/**
* #ORM\Column(type="string", length=250)
*/
protected $comments;
/**
* #ORM\ManyToMany(targetEntity="Application\Sonata\MediaBundle\Entity\Media", mappedBy="orders")
*/
protected $medias;
public function __construct() {
$this->medias = 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(\PL\CompanyBundle\Entity\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 setComments($comments) {
$this->comments = $comments;
}
public function getComments() {
return $this->comments;
}
public function setMedias(\Application\Sonata\MediaBundle\Entity\Media $media) {
$this->medias[] = $media;
}
public function addMedia(\Application\Sonata\MediaBundle\Entity\Media $media) {
$this->medias[] = $media;
}
public function getMedias() {
return $this->medias;
}
}
Changed the configureFormFields in OrderAdmin.php file as follow:
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' => array(
"empty_value" => "Seleccione una opción",
"0" => "Ninguno",
"1" => "Proceso de Fabricacion",
"2" => "Pickup en destino",
"3" => "A la espera de recojo por cliente",
"4" => "Carga en transito",
"5" => "Carga arribada",
"6" => "En proceso de aduana",
"7" => "Entregado a cliente",
"8" => "En bodega"
), "required" => true, 'label' => 'Estado de la carga'))
->add('eta', null, array('label' => 'ETD'))
->add('etd', null, array('label' => 'ETA'))
->add('transport_media', 'choice', array('choices' => array("empty_value" => "Seleccione una opción", "0" => "EXW", "1" => "Maritimo", "2" => "Aereo"), "required" => true, 'label' => 'Via de Transporte'))
->add('incoterm', 'choice', array('choices' => array(
"empty_value" => "Seleccione una opción",
"0" => "Ninguno",
"1" => "EWX",
"2" => "FOB",
"3" => "CIF",
"4" => "DDP"
), "required" => true, 'label' => 'Incoterm'))
->add('comments', null, array('label' => 'Comentarios'))
->add('medias', 'sonata_type_collection', array(
'label' => 'Documentos',
'type_options' => array('delete' => true)), array(
'edit' => 'inline', 'inline' => 'table', 'sortable' => 'position')
);
}
But this doesn't work since I can't upload any file and this is what I want upload many files from the same form and attach them to the order I'm creating. See the attached images for a visual I get when I access the create action:
What I'm missing?
For your solution to have multiple images for your company admin you have to organize your relation like there will be one junction entity which will point to sonata media entity in a ManyToOne relation and also to your products entity in a ManyToOne relation as well i have created this type of collection for one of needs for footer widgets which can have multiple images so you can map it for your products images as well in a similar way.
Footer Entity contains a property named as links which points to a junction entity FooterWidgetsHasMedia in OneToMany way,junction entity (FooterWidgetsHasMedia ) holds the relation to sonata media in addition i need multiple images for my each footer object and also for each image a in need a hover image too so my junction entity basically holds two properties that points to sonata media
FooterWidgets
/**
* #Assert\NotBlank()
* #ORM\OneToMany(targetEntity="Traffic\WidgetsBundle\Entity\FooterWidgetsHasMedia", mappedBy="footerWidget",cascade={"persist","remove"} )
*/
protected $links;
/**
* Remove widgetImages
*
* #param \Application\Sonata\MediaBundle\Entity\Media $widgetImages
*/
public function removeLinks(\Traffic\WidgetsBundle\Entity\FooterWidgetsHasMedia $links)
{
$this->links->removeElement($links);
}
/**
* Get widgetImages
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getLinks()
{
return $this->links;
}
/**
* {#inheritdoc}
*/
public function setLinks($links)
{
$this->links = new ArrayCollection();
foreach ($links as $footerWidget) {
$this->addLinks($footerWidget);
}
}
/**
* {#inheritdoc}
*/
public function addLinks(\Traffic\WidgetsBundle\Entity\FooterWidgetsHasMedia $links)
{
$links->setFooterWidget($this);
$this->links[] = $links;
}
Now my junction entity will points back to FooterWidgets and sonata media entity
FooterWidgetsHasMedia
Definitions of properties
/**
* #var \Application\Sonata\MediaBundle\Entity\Media
* #Assert\NotBlank()
* #ORM\ManyToOne(targetEntity="Application\Sonata\MediaBundle\Entity\Media", cascade={"persist"}, fetch="LAZY")
* #ORM\JoinColumn(name="media_id", referencedColumnName="id")
*/
protected $media;
/**
* #var \Application\Sonata\MediaBundle\Entity\Media
* #Assert\NotBlank()
* #ORM\ManyToOne(targetEntity="Application\Sonata\MediaBundle\Entity\Media", cascade={"persist"}, fetch="LAZY")
* #ORM\JoinColumn(name="media_hover_id", referencedColumnName="id")
*/
protected $mediaHover;
/**
* #var \Traffic\WidgetsBundle\Entity\FooterWidgets
* #Assert\NotBlank()
* #ORM\ManyToOne(targetEntity="Traffic\WidgetsBundle\Entity\FooterWidgets", cascade={"persist","remove"} ,inversedBy="links", fetch="LAZY" )
* #ORM\JoinColumn(name="footer_widget_id", referencedColumnName="id",nullable=true)
*/
protected $footerWidget;
/**
* #var integer
* #ORM\Column(name="position", type="integer")
*/
protected $position;
/**
* #var boolean
* #ORM\Column(name="enable", type="boolean")
*/
protected $enabled;
Generate getters and setters for above properties
Now you have to create new admin for your collection which references to junction entity FooterWidgetsHasMedia and configureFormFields will look something like below
FooterWidgetsHasMediaAdmin
protected function configureFormFields(FormMapper $formMapper)
{
$link_parameters = array();
if ($this->hasParentFieldDescription()) {
$link_parameters = $this->getParentFieldDescription()->getOption('link_parameters', array());
}
if ($this->hasRequest()) {
$context = $this->getRequest()->get('context', null);
if (null !== $context) {
$link_parameters['context'] = $context;
}
}
$formMapper
->add('media', 'sonata_type_model_list', array('required' => false), array(
'link_parameters' => $link_parameters
))
->add('mediaHover', 'sonata_type_model_list', array('required' => false), array(
'link_parameters' => $link_parameters
))
->add('enabled', null, array('required' => false))
->add('link', 'text', array('required' => false))
->add('position', 'hidden')
;
}
And your company admin will have a new field in configureFormFields
FooterWidgetsAdmin
->add('links', 'sonata_type_collection', array(
'cascade_validation' => false,
'type_options' => array('delete' => false),
), array(
'edit' => 'inline',
'inline' => 'table',
'sortable' => 'position',
'link_parameters' => array('context' => 'widgets'),
'admin_code' => 'sonata.admin.footer_widgets_has_media' /*here provide service name for junction admin */
)
)
Register admin service for your new admin as
sonata.admin.footer_widgets_has_media:
class: Traffic\WidgetsBundle\Admin\FooterWidgetsHasMediaAdmin
tags:
- { name: sonata.admin, manager_type: orm, group: "Widgets", label: "Footer Widgets Section Media" , show_in_dashboard: false }
arguments:
- ~
- Traffic\WidgetsBundle\Entity\FooterWidgetsHasMedia
- ~
calls:
- [ setTranslationDomain, [TrafficWidgetsBundle]]
Demo snap shot
You can find full code demo here Git Hub hope it makes sense

Doctrine Mapping Exception in Zend Framework 2's Project while using hydrator

I am trying to implement doctrine hydrator in my zend 2 project. I am using doctrine's official documentation
I am getting two warning and one error. Following is the warning at top of page.
Warning: Missing argument 2 for DoctrineModule\Stdlib\Hydrator\DoctrineObject::__construct(), called in /path/to/my/project/module/Notes/src/Notes/Form/AssignForm.php on line 16 and defined in /path/to/my/project/vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/DoctrineObject.php on line 71
Notice: Undefined variable: targetClass in /path/to/my/project/vendor/doctrine/doctrine-module/src/DoctrineModule/Stdlib/Hydrator/DoctrineObject.php on line 76
Here is the error:
An error occurred
An error occurred during execution; please try again later.
Additional information:
Doctrine\Common\Persistence\Mapping\MappingException
File:
/path/to/my/project/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/MappingException.php:96
Message:
Class '' does not exist
Here is my entity:
use Doctrine\ORM\Mapping as ORM;
/** #ORM\Entity */
class Assigned{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
protected $id;
/** #ORM\Column(type="string")
* #access protected
*/
protected $loan_number;
/** #ORM\Column(type="string")
* #access protected
*/
protected $claim_status;
/** #ORM\Column(type="datetime")
* #access protected
*/
protected $hold_date;
/** #ORM\Column(type="datetime")
* #access protected
*/
protected $vsi_date;
/** #ORM\Column(type="integer")
* #access protected
*/
protected $hold_status;
/** #ORM\Column(type="integer")
* #access protected
*/
protected $vsi_status;
/**
* #param string $id
* #return Assign
*/
// id should be auto incremental in database.
/*
public function setId($id)
{
$this->id = $id;
return $this;
}
*/
/**
* #return string $id;
*/
public function getId()
{
return $this->id;
}
/**
* #param string $loan_number
* #access public
* #return Assigned
*/
public function setLoanNumber($loan_number)
{
$this->loan_number = $loan_number;
return $this;
}
/**
* #return string $loan_number
*/
public function getLoanNumber()
{
return $this->loan_number;
}
/**
* #param string $claim_status
* #access public
* #return Assigned
*/
public function setClaimStatus($claim_status)
{
$this->claim_status = $claim_status;
return $this;
}
/**
* #return string $claim_status;
*/
public function getClaimStatus()
{
return $this->claim_status;
}
/**
* #param datetime $hold_date
* #access public
* #return Assigned
*/
public function setHoldDate($hold_date)
{
$this->hold_date = new \DateTime($hold_date);
return $this;
}
/**
* #return datetime $hold_date;
*/
public function getHoldDate()
{
return $this->hold_date;
}
/**
* #param datetime $vsi_date
* #access public
* #return Assigned
*/
public function setVsiDate($vsi_date)
{
$this->vsi_date = new \DateTime($vsi_date);
return $this;
}
/**
* #return datetime $vsi_date;
*/
public function getVsiDate()
{
return $this->vsi_date;
}
/**
* #param integer $hold_status
* #access public
* #return Assigned
*/
public function setHoldStatus($hold_status)
{
$this->hold_status = $hold_status;
return $this;
}
/**
* #return integer $hold_status;
*/
public function getHoldStatus($hold_status)
{
return $this->hold_status;
}
/**
* #param integer $vsi_status
* #access public
* #return Assigned
*/
public function setVsiStatus($vsi_status)
{
$this->vsi_status = $vsi_status;
return $this;
}
/**
* #return integer $vsi_status;
*/
public function getVsiStatus()
{
return $this->vsi_status;
}
}
Here is my Controller
<?php
namespace Notes\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Doctrine\ORM\EntityManager;
use Application\Entity\Assigned;
use Notes\Form\AssignForm;
use Notes\Form\NotesFieldset;
class NotesController extends AbstractActionController
{
protected $objectManager;
public function indexAction()
{
return new ViewModel();
}
public function addAction()
{
// Get your ObjectManager from the ServiceManager
$objectManager = $this->getOBjectManager();
$form = new AssignForm($objectManager);
// Create a new, empty entity and bind it to the form
$assigned = new Assigned();
$form->bind($assigned);
if ($this->request->isPost()) {
$form->setData($this->request->getPost());
if ($form->isValid()) {
$objectManager->persist($assigned);
$objectManager->flush();
}
}
return array('form' => $form);
}
public function getOBjectManager(){
if(!$this->objectManager){
$this->objectManager = $this->getServiceLocator()
->get('Doctrine\ORM\EntityManager');
}
return $this->objectManager;
}
}
Here is my form class:
<?php
namespace Notes\Form;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\Form\Form;
use Notes\Form\NotesFieldset;
class AssignForm extends Form
{
public function __construct(ObjectManager $objectManager)
{
parent::__construct('assigned');
// The form will hydrate an object of type "BlogPost"
$this->setHydrator(new DoctrineHydrator($objectManager));
// Add the user fieldset, and set it as the base fieldset
$notesFieldset = new NotesFieldset($objectManager);
$notesFieldset->setUseAsBaseFieldset(true);
$this->add($notesFieldset);
// … add CSRF and submit elements …
// Optionally set your validation group here
}
}
Here is the Fieldset class
<?php
namespace Notes\Form;
use Application\Entity\Assigned;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;
class NotesFieldset extends Fieldset implements InputFilterProviderInterface
{
protected $inputFilter;
public function __construct(ObjectManager $objectManager)
{
parent::__construct('assigned');
$this->setHydrator(new DoctrineHydrator($objectManager))
->setObject(new Assigned());
$this->add(array(
'type' => 'Zend\Form\Element\Hidden',
'name' => 'id'
));
$this->add(array(
'type' => 'Zend\Form\Element\Text',
'name' => 'loan_number',
'options' => array(
'label' => 'Loan Number'
)
));
$this->add(array(
'type' => 'Zend\Form\Element\Text',
'name' => 'claim_status',
'options' => array(
'label' => 'Claim Status'
)
));
$this->add(array(
'type' => 'Zend\Form\Element\Text',
'name' => 'hold_date',
'options' => array(
'label' => 'Hold Date'
)
));
$this->add(array(
'type' => 'Zend\Form\Element\Text',
'name' => 'vsi_date',
'options' => array(
'label' => 'VSI Date'
)
));
$this->add(array(
'type' => 'Zend\Form\Element\Text',
'name' => 'hold_status',
'options' => array(
'label' => 'Hold Status'
)
));
$this->add(array(
'name' => 'vsi_status',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => 'VSI Status',
),
));
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Go',
'id' => 'submitbutton',
),
));
}
/**
* Define InputFilterSpecifications
*
* #access public
* #return array
*/
public function getInputFilterSpecification()
{
return array(
'id' => array(
'required' => false
),
'name' => array(
'required' => true
),
'loan_number' => array(
'required' => true
),
'claim_status' => array(
'required' => true
),
'hold_date' => array(
'required' => true
),
'hold_status' => array(
'required' => true
),
'vsi_date' => array(
'required' => true
),
'hold_status' => array(
'required' => true
),
);
}
}
It is saying : Class '' does not exist. But the class name is not given in the message.
Beside this
I am using these library for doctrine in my composer.
"doctrine/doctrine-orm-module": "0.7.*",
"doctrine/migrations": "dev-master",
"doctrine/common": "2.4.*#dev",
"doctrine/annotations": "1.0.*#dev",
"doctrine/data-fixtures": "1.0.*#dev",
"doctrine/cache": "1.0.*#dev",
"zf-commons/zfc-user-doctrine-orm": "dev-master",
Please tell me what is missing in my implementation. I have got one open issue in github for doctrine with incomplete example docs. Here is link of issue. If this is the case, then please suggest me ho to implement correctly.
Your missing the target class parameter:
$this->setHydrator(new DoctrineHydrator($objectManager));
Should be:
$this->setHydrator(
new DoctrineHydrator(
$objectManager,
'the\target\entity' // <-- the target entity class name
)
);
The targetClass is used within the DoctrineObject to fetch the metadata of the hydrated entity. You can see this in DoctrineModule\Stdlib\Hydrator\DoctrineObject::__construct():
$this->metadata = $objectManager->getClassMetadata($targetClass);

Categories