I've been playing around with Zend and Doctrine incorporated into it for the past week or so. I've gotten the hang of basic inserts and selects, and can also use DQL to select from joined tables. The problem I'm having is persisting associated entities. Error I'm getting is this: (path)htdocs\vendor\doctrine\common\lib\Doctrine\Common\Persistence\Mapping\MappingException.php:96 with the message 'Class "" does not exist'.
My code is below...
Here is the main entity (the one on the "many" side)
namespace Project\Entity;
use Doctrine\ORM\Mapping as ORM;
use Zend\InputFilter\Factory as InputFactory;
/**
* ClientUser
*
* #ORM\Table()
* #ORM\Entity
*/
class ClientUser extends SystemUser
{
/**
* #var integer
*
* #ORM\OneToOne(targetEntity="SystemUser", cascade={"persist", "remove"})
* #ORM\JoinColumn(name="id", referencedColumnName="id")
*/
private $id;
/**
* #var integer
*
* #ORM\ManyToOne(targetEntity="Client", cascade={"persist", "remove"})
* #ORM\JoinColumn(name="client", referencedColumnName="id")
*/
private $client;
protected $_inputFilter;
//Other stuff here...
}
Here is the "Client" associated entity...
namespace Project\Entity;
use Doctrine\ORM\Mapping as ORM;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\Factory as InputFactory;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
/**
* Client
*
* #ORM\Table()
* #ORM\Entity
*/
class Client implements InputFilterAwareInterface
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="client_name", type="string", length=255)
*/
private $clientName;
/**
* #var integer
*
* #ORM\Column(name="loggable_hours", type="integer")
*/
private $loggableHours;
/**
* #var float
*
* #ORM\Column(name="normal_rate", type="decimal", scale=2)
*/
private $normalRate;
/**
* #var float
*
* #ORM\Column(name="critical_rate", type="decimal", scale=2)
*/
private $criticalRate;
/**
* #var string
*
* #ORM\Column(name="start_date", type="string")
*/
private $startDate;
/**
* #var boolean
*
* #ORM\Column(name="enabled", type="boolean")
*/
private $enabled;
/**
* #var integer
*
* #ORM\Column(name="critical_hours", type="integer")
*/
private $criticalHours;
/**
*
* #var type
*/
protected $_inputFilter;
//Other stuff (getters,setters, etc)
}
The ClientUser has a one-to-one relationship with the following:
namespace Project\Entity;
use Doctrine\ORM\Mapping as ORM;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\Factory as InputFactory;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
/**
* SystemUser
*
* #ORM\Table()
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="user_type", type="integer")
* #ORM\DiscriminatorMap({1 = "DeveloperUser", 2 = "ClientUser"})
*
*/
class SystemUser implements InputFilterAwareInterface {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="username", type="string", length=100, unique=true)
*/
private $username;
/**
* #var string
*
* #ORM\Column(name="password", type="string", length=255)
*/
private $password;
/**
* #var string
*
* #ORM\Column(name="user_first_name", type="string", length=255)
*/
private $userFirstName;
/**
* #var string
*
* #ORM\Column(name="user_surname", type="string", length=255)
*/
private $userSurname;
/**
* #ORM\Column(type="string", length=32)
*/
private $salt;
/**
* #var \DateTime
*
* #ORM\Column(name="last_login", type="datetime")
*/
private $lastLogin = '0000-00-00 00:00:00';
/**
* #var bool
*
* #ORM\Column(name="enabled", type="boolean", options={"default" = 1})
*/
private $enabled = 1;
/**
* For the input filter...
*
* #var InputFilter
*/
protected $_inputFilter;
//The rest...
}
I have absolutely no idea what could be wrong here... Just for completeness, here is the controller "add" action...
public function addAction() {
//To add clients
$form = new ClientUserForm($this->getServiceLocator()
->get('Doctrine\ORM\EntityManager'));
$form->get('submit')->setValue('Add');
$request = $this->getRequest();
if ($request->isPost()) {
$clientUser = new ClientUser();
$form->setInputFilter($clientUser->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()) {
$clientUser->populate($form->getData());
/**
*This bottom line is where I get the exception!
*/
$this->getEntityManager()->persist($clientUser);
$this->getEntityManager()->flush();
//Redirect
return $this->redirect()->toRoute('client_user');
}
}
return array ('form' => $form);
}
Any help would be awesome! If I just knew which class "" is supposed to be, I'd probably be in a better place than I am now!
Thanks ladies and gents, you guys rock!
EDIT-
Forgot to add these 2 PHP warnings...
Warning: spl_object_hash() expects parameter 1 to be object, integer given in (path)\htdocs\vendor\doctrine\orm\lib\Doctrine\ORM\UnitOfWork.php on line 1588
Warning: get_class() expects parameter 1 to be object, integer given in (path)\vendor\doctrine\orm\lib\Doctrine\ORM\UnitOfWork.php on line 1596
Im not sure of what could be happening, but i find something that i dont understand. When you states the inheritance, you use:
* #ORM\DiscriminatorColumn(name="user_type", type="integer")
* #ORM\DiscriminatorMap({1 = "DeveloperUser", 2 = "ClientUser"})
but
there isnt a class called DeveloperUser
and also
are you sure that in the database, all user_type are just 1 or 2? (no null, not 0, etc)
Related
I have these 3 entitites
Users.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Users
*
* #ORM\Table(name="users")
* #ORM\Entity(repositoryClass="AppBundle\Repository\UsersRepository")
*/
class Users
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=255, unique=true)
*/
private $email;
/**
* #var string
*
* #ORM\Column(name="password", type="string", length=20)
*/
private $password;
/**
* #var string
*
* #ORM\Column(name="phone", type="string", length=20)
*/
private $phone;
/**
* #var string
*
* #ORM\Column(name="type", type="string")
*/
private $type;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #var int
*
* #ORM\Column(name="feedback", type="integer")
*/
private $feedback;
/**
* #var string
*
* #ORM\Column(name="picture", type="blob")
*/
private $picture;
/**
* #var int
*
* #ORM\Column(name="rating", type="integer", length=255)
*/
private $rating;
/**
* #var string
*
* #ORM\Column(name="info", type="text")
*/
private $info;
/**
* #var \DateTime
*
* #ORM\Column(name="datecreated", type="datetime")
*/
private $datecreated;
/**
* #ORM\Column(name="is_active", type="boolean")
*/
private $isActive;
}
client.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* client
*
* #ORM\Table(name="client")
* #ORM\Entity(repositoryClass="AppBundle\Repository\clientRepository")
*/
class client extends Users
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var int
*
* #ORM\Column(name="numberofjobsposted", type="integer")
*/
private $numberofjobsposted;
/**
* #var string
*
* #ORM\Column(name="clienttype", type="string", length=255)
*/
private $clienttype;
}
sprovider.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* sprovider
*
* #ORM\Table(name="sprovider")
* #ORM\Entity(repositoryClass="AppBundle\Repository\sproviderRepository")
*/
class sprovider extends Users
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var array
*
* #ORM\Column(name="interestedin", type="simple_array")
*/
private $interestedin;
/**
* #var int
*
* #ORM\Column(name="numofsuccjobs", type="integer")
*/
private $numofsuccjobs;
/**
* #var string
*
* #ORM\Column(name="sprovidertype", type="string", length=255)
*/
private $sprovidertype;
/**
* #var string
*
* #ORM\Column(name="address", type="string", length=255)
*/
private $address;
/**
* #var string
*
* #ORM\Column(name="postcode", type="string", length=255)
*/
private $postcode;
}
So I achieved that the extends statement provides the Users properties in the client and sprovider tables in MySQL. That's awesome. What I want now is to make the relations so that when I add a new client for example, both the tables Users and client add a new user/client in MySQL, and they have same id too.
the type() property in the Users entity i would like to be optional for the type of user I create. Example : I create a new client and in the Users table in MySQL the type is set to "CLIENT".
I read this and so far I think it has to be ManyToMany relation but It's quite confusing to me.
How to make those relations in the entities and then how to use them in the controller? If possible, please provide an example.
I think you're confused about the reasons to use inheritance.
The idea is that you have base class, in this case User, and that can be extended to provide variations of that class, in this case client (you should capitalise this) and sprovider.
Ideally, you would not have a User table, only the other 2.
In doctrine, this is called a mapped super-class.
A mapped superclass is an abstract or concrete class that provides persistent entity state and mapping information for its subclasses, but which is not itself an entity. Typically, the purpose of such a mapped superclass is to define state and mapping information that is common to multiple entity classes.
see the documentation here
you can link properties using relationships, this is their example.
<?php
/** #MappedSuperclass */
class MappedSuperclassBase
{
/** #Column(type="integer") */
protected $mapped1;
/** #Column(type="string") */
protected $mapped2;
/**
* #OneToOne(targetEntity="MappedSuperclassRelated1")
* #JoinColumn(name="related1_id", referencedColumnName="id")
*/
protected $mappedRelated1;
// ... more fields and methods
}
/** #Entity */
class EntitySubClass extends MappedSuperclassBase
{
/** #Id #Column(type="integer") */
private $id;
/** #Column(type="string") */
private $name;
// ... more fields and methods
}
I'm trying to get all Tickets with "Destinataire" equal to a "Compte":
$ret = $repository->findByDestinataires($compte->getId());
My problem is that I get an error:
An exception occurred while executing 'SELECT t0.id AS id_1, t0.Titre AS Titre_2, t0.DateCreation AS DateCreation_3, t0.DateButoire AS DateButoire_4, t0.DateFin AS DateFin_5, t0.Priorite AS Priorite_6, t0.Commentaire AS Commentaire_7, t0.Statut AS Statut_8, t0.emeteur_id AS emeteur_id_9, t0.client_id AS client_id_10 FROM ticket t0 WHERE ticket_compte.compte_id = ?' with params [1]:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'ticket_compte.compte_id' in 'where clause'
I have two tables like this:
Compte:
namespace CommonBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Compte
*
* #ORM\Table(name="compte")
* #ORM\Entity(repositoryClass="CommonBundle\Repository\CompteRepository")
*/
class Compte
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* #var string
*
* #ORM\Column(name="Nom", type="string", length=80)
*/
public $nom;
/**
* #var string
*
* #ORM\Column(name="Prenom", type="string", length=80)
*/
public $prenom;
/**
* #var string
*
* #ORM\Column(name="Fonction", type="string", length=80)
*/
public $fonction;
/**
* #var string
*
* #Assert\NotBlank()
* #ORM\Column(name="Pseudo", type="string", length=80)
*/
public $pseudo;
/**
* #var string
*
* #Assert\NotBlank()
* #ORM\Column(name="MotDePasse", type="string", length=80)
*/
public $motDePasse;
/**
* #ORM\ManyToOne(targetEntity="CommonBundle\Entity\Profil", cascade={"persist"})
* #ORM\JoinColumn(nullable=false)
*/
public $profil;
/**
* #ORM\ManyToMany(targetEntity="CommonBundle\Entity\Ticket", cascade={"persist"})
*/
public $tickets;
}
Ticket:
namespace CommonBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Ticket
*
* #ORM\Table(name="ticket")
* #ORM\Entity(repositoryClass="CommonBundle\Repository\TicketRepository")
*/
class Ticket
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* #var string
*
* #ORM\Column(name="Titre", type="string", length=80)
*/
public $titre;
/**
* #var \DateTime
*
* #ORM\Column(name="DateCreation", type="datetimetz")
*/
public $dateCreation;
/**
* #var \DateTime
*
* #ORM\Column(name="DateButoire", type="datetimetz")
*/
public $dateButoire;
/**
* #var \DateTime
*
* #ORM\Column(name="DateFin", type="datetimetz", nullable=true)
*/
public $dateFin;
/**
* #var int
*
* #ORM\Column(name="Priorite", type="integer")
*/
public $priorite;
/**
* #var string
*
* #ORM\Column(name="Commentaire", type="text")
*/
public $commentaire;
/**
* #var int
*
* #ORM\Column(name="Statut", type="integer")
*/
public $statut;
/**
* #ORM\ManyToOne(targetEntity="CommonBundle\Entity\Compte", cascade={"persist"})
* #ORM\JoinColumn(nullable=false)
*/
public $emeteur;
/**
* #ORM\ManyToMany(targetEntity="CommonBundle\Entity\Compte", cascade={"persist"})
* #ORM\JoinColumn(nullable=true)
*/
public $destinataires;
/**
* #ORM\ManyToOne(targetEntity="CommonBundle\Entity\Client")
* #ORM\JoinColumn(nullable=true)
*/
public $client;
}
And there links:
compte_ticket //for "emeteure"
->ticket_id compte_id
ticket_compte //for "destinataires"
->ticket_id compte_id
I've tried the same request directly on the server and got the same error.
Is it the PHP code that is wrong?
Or maybe the entities...
INFO:
I've not put all the get/set from Compte or Ticket on purpose. If it's needed I'll edit.
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/working-with-objects.html
It seem the that the shortcut method you are using (findByDestinataires) is only designed to get related records from the owning side of a relationship and hence only one 2 many not many 2 many.
You will need to create a custom query for this.
Here is one I knocked up quickly, add the following function to your TicketRepository
public function getTicketsByCompteId($compte_id)
{
return $this->getEntityManager()
->createQuery(
"SELECT t, d FROM AppBundle:Ticket t
LEFT JOIN t.destinataires d
WHERE
d.id = :compte_id"
)
->setParameter('compte_id', $compte_id)
->getResult();
}
And then call it from your controller:
$ret = $repository->getTicketsByCompteId($compte->getId());
Sounds like the foreign key field is not created. Did you run
php app/console doctrine:schema:update --force --dump-sql
After adding your relationships?
I'm using this query builder in my repository:
public function findByCityCategory($city, $category)
{
$qb = $this->createQueryBuilder('e')
->select(['e.id','e.title','e.address', 'e.lat', 'e.lng', 'e.siteUrl', 'e.phoneNo', 'w', 'd.id as category', 'avg(r.rating) as rating'])
->innerJoin('e.workingTimes', 'w')
->innerJoin('e.category', 'd')
->where('d.id = :categoryId')
->andWhere('e.city = :cityId')
->leftJoin('e.ratings', 'r')
->groupBy('r.place')
->setParameter('categoryId', $category)
->setParameter('cityId', $city);
return $qb->getQuery()->getResult();
}
But when I try to execute it, I get:
"message": "[Semantical Error] line 0, col -1 near 'SELECT e.id,': Error: Cannot select entity through identification variables without choosing at least one root entity alias.",
"class": "Doctrine\\ORM\\Query\\QueryException",
I looked for similar problems here and here but none of these worked for me. My Entities looks like that:
<?php
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Place
*
* #ORM\Table(name="place")
* #ORM\Entity(repositoryClass="AppBundle\Repository\PlaceRepository")
*/
class Place
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
* #Assert\NotBlank()
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #var string
* #Assert\NotBlank()
* #ORM\Column(name="address", type="string", length=255)
*/
private $address;
/**
* #var float
* #Assert\NotBlank()
* #ORM\Column(name="lat", type="float")
*/
private $lat;
/**
* #var float
* #Assert\NotBlank()
* #ORM\Column(name="lng", type="float")
*/
private $lng;
/**
* #var string
*
* #ORM\Column(name="description", type="text", nullable=true)
*/
private $description;
/**
* #var string
*
* #ORM\Column(name="fb_page", type="string", length=255, nullable=true)
*/
private $fbPage;
/**
* #Assert\NotBlank()
* #ORM\ManyToOne(targetEntity="City")
* #ORM\JoinColumn(name="city_id", referencedColumnName="id")
*/
private $city;
/**
* #Assert\NotBlank()
* #ORM\ManyToOne(targetEntity="Category", inversedBy="places")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $category;
/**
* #var string
*
* #ORM\Column(name="site_url", type="string", length=255, nullable=true)
*/
private $siteUrl;
/**
* #ORM\ManyToMany(targetEntity="WorkingTime", cascade={"persist"})
* #ORM\JoinTable(name="places_workingtimes",
* joinColumns={#ORM\JoinColumn(name="place_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="workingtime_id", referencedColumnName="id", unique=true)}
* )
*/
private $workingTimes;
/**
* #var string
* #ORM\Column(name="phone_no", type="string", length=255, nullable=true)
*
*/
private $phoneNo;
/**
* #ORM\OneToMany(targetEntity="Rating", mappedBy="place")
*/
private $ratings;
}
`
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Rating
*
* #ORM\Table(name="rating")
* #ORM\Entity(repositoryClass="AppBundle\Repository\RatingRepository")
*/
class Rating
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var int
*
* #ORM\Column(name="rating", type="smallint")
*/
private $rating;
/**
* #ORM\ManyToOne(targetEntity="Place", inversedBy="ratings")
* #ORM\JoinColumn(name="place_id", referencedColumnName="id")
*/
private $place;
/**
* #ORM\ManyToOne(targetEntity="User")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
This error only occurs, when I try to select 'w'. So, how can I join this collection of objects?
If you want to only get partial fields from your entity, you have to use the PARTIAL keyword as explained on documentation: http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/partial-objects.html
It would be better to load all the fields instead of some if you use the ORM query builder, because the ORM is made to work with objects. If you need only few fields, it can be a better practice to use the native SQL query builder.
I've managed to do it by excluding unnecessary fields from Place entity using JMS Serializer's Exclude() annotation instead of selecting required fields from entity.
I'm trying to create a "OneToMany" bidirectional association in my project but when I execute "doctrine:schema:update" nothing happens.
If I create this association directly from Sequel Pro and run the update schema command, that changes dissapear... :/
The relations is:
- One "id" from Customers Table with many "customer_id" form Control table.
Here is the Customers code:
<?php
namespace Ourentec\CustomersBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* Customers
*
* #ORM\Table()
* #ORM\Entity
*/
class Customers
{
/* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=100)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="lastname", type="string", length=100)
*/
private $lastname;
/**
* #var string
*
* #ORM\Column(name="address", type="text")
*/
private $address;
/**
* #var string
*
* #ORM\Column(name="phone", type="string", length=100)
*/
private $phone;
/**
* #var string
*
* #ORM\Column(name="pass", type="string", length=100)
*/
private $pass;
/**
* #var string
*
* #ORM\Column(name="tasks", type="text")
*/
private $tasks;
/**
* #var string
*
* #ORM\Column(name="status", type="string", length=100)
*/
private $status;
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=100)
*/
private $email;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="datetime")
*/
private $date;
/**
* #var string
*
* #ORM\Column(name="location", type="string", length=100)
*/
private $location;
/**
* #ORM\OneToMany(targetEntity="Control", mappedBy="customers")
*/
private $customer_id;
public function __construct()
{
$this->customer_id = new ArrayCollection();
}
And the Control code:
<?php
namespace Ourentec\CustomersBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Control
*
* #ORM\Table()
* #ORM\Entity
*/
class Control
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="customer_id", type="integer")
*
* #ORM\ManyToOne(targetEntity="Customers", inversedBy="control")
* #ORM\JoinColumn(name="customer_id", referencedColumnName="id")
*/
private $customerId;
/**
* #var integer
*
* #ORM\Column(name="user_id", type="integer")
*/
private $userId;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="datetime")
*/
private $date;
/**
* #var integer
*
* #ORM\Column(name="seen", type="smallint")
*/
private $seen;
I followed the documentation from this 2 websites
http://symfony.com/doc/current/book/doctrine.html
http://librosweb.es/libro/symfony_2_x/capitulo_8/relaciones_y_asociaciones_de_entidades.html
But I don't know why it does not work..
Any idea will be appreciated :)
Mapping are not correct, I will try to explain how it works.
In Customers entity (you should rename it to Customer, entites names are singular)
/**
* #ORM\OneToMany(targetEntity="Control", mappedBy="customer")
*/
private $controls;
Mapped by option defines field name in the other entity.
/**
* #var integer
*
* #ORM\Column(name="customer_id", type="integer")
*
* #ORM\ManyToOne(targetEntity="Customers", inversedBy="controls")
* #ORM\JoinColumn(name="customer_id", referencedColumnName="id")
*/
private $customer;
Same thing with inversedBy.
In Customers entity you also need to init controls var as an ArrayCollection:
public function __construct()
{
$this->controls = new ArrayCollection();
}
With these mappings schema should be updated correctly.
For more info, check doctrine docs.
I'm trying to use JMS serializer in my application (not Symfony) and would like to deserialize a JSON object to the Doctrine Entity.
The plain properties are getting properly deserialized, but I can't get the ArrayCollections to work.
This is an excerpt of my product JSON:
{
"id": 2,
"name": "Shirt blue",
"attributeValues": [
{
"id": 4,
"title": "S",
"attributeId": 2
},
{
"id": 7,
"title": "Eterna",
"attributeId": 3
}
]
}
This is my Product entity:
<?php
namespace Vendor\App\Common\Entities;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
* #ORM\Table(name="product")
* #JMS\ExclusionPolicy("all")
*/
class Product extends AbstractEntity {
/**
* #var int $id
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
* #ORM\Column(name="id", type="integer", nullable=false)
* #JMS\Groups({"search"})
* #JMS\Expose
* #JMS\Type("integer")
*/
protected $id;
/**
* #var string $name
* #ORM\Column(name="name", type="string", nullable=false)
* #JMS\Expose
* #JMS\Groups({"search"})
* #JMS\Type("string")
*/
protected $name;
/**
* #var ArrayCollection $attributeValues
* #ORM\ManyToMany(targetEntity="Vendor\App\Common\Entities\Attribute\Value")
* #ORM\JoinTable(name="products_values",
* joinColumns={#ORM\JoinColumn(name="product_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="value_id", referencedColumnName="id")}
* )
* #JMS\Expose
* #JMS\MaxDepth(2)
* #JMS\Groups({"search"})
* #JMS\Type("ArrayCollection<Vendor\App\Common\Entities\Attribute\Value>")
*/
protected $attributeValues;
public function __construct() {
$this->attributeValues = new ArrayCollection();
}
/**
* #return ArrayCollection
*/
public function getAttributeValues() {
return $this->attributeValues;
}
/**
* #param ArrayCollection $attributeValues
*/
public function setAttributeValues($attributeValues) {
$this->attributeValues = $attributeValues;
}
/**
* #param Value $attributeValue
*/
public function addAttributeValue($attributeValue) {
$this->attributeValues->add($attributeValue);
}
/**
* #param Value $attributeValue
*/
public function removeAttributeValue($attributeValue) {
$this->attributeValues->removeElement($attributeValue);
}
}
This is my Value entity that should be deserialized in the ArrayCollection:
<?php
namespace Vendor\App\Common\Entities\Attribute;
use Vendor\App\Common\Entities\AbstractEntity,
Doctrine\ORM\Mapping as ORM,
JMS\Serializer\Annotation as JMS;
/**
* #ORM\Entity
* #ORM\Table(name="attribute_value")
* #JMS\ExclusionPolicy("all")
*/
class Value extends AbstractEntity {
/**
* #var int $id
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
* #ORM\Column(name="id", type="integer", nullable=false)
* #JMS\Expose
* #JMS\Groups({"search"})
* #JMS\Type("integer")
*/
protected $id;
/**
* #var string $title
* #ORM\Column(name="title", type="string", nullable=false)
* #JMS\Expose
* #JMS\Groups({"search"})
* #JMS\Type("string")
*/
protected $title;
/**
* #var int $attributeId
* #ORM\Column(name="attribute_id", type="integer", nullable=false)
* #JMS\Expose
* #JMS\Groups({"search"})
* #JMS\Type("integer")
*/
protected $attributeId;
/**
* OWNING SIDE
* #var \Vendor\App\Common\Entities\Attribute $attribute
* #ORM\ManyToOne(targetEntity="Vendor\App\Common\Entities\Attribute", inversedBy="values")
* #ORM\JoinColumn(name="attribute_id", referencedColumnName="id")
* #JMS\Expose
* #JMS\Groups({"search"})
* #JMS\Type("Vendor\App\Common\Entities\Attribute")
*/
protected $attribute;
//Getters and setters ...
}
Just trying to simply deserialize the entity:
$serializer = SerializerBuilder::create()->build();
$entity = $serializer->deserialize($sourceJson, Product::class, 'json');
But the attributeValue ArrayCollection stays empty. What am I missing?
I found the solution. JMS has a default naming strategy which converts camelcase to underscore notation.
Default is that the naming strategy either looks for the annotation #SerializedName and if that is not set, it will convert CamelCase to underscore.
So the property just got ignored because it didn't match the expected name. Of course, it would be better if there was an error or a notification which gives a hint here on where to search for the problem (something like unknown property would have been nice).
$serializer = SerializerBuilder::create()->setPropertyNamingStrategy(new IdenticalPropertyNamingStrategy())->build();
Was the solution.