I work with some related objects, and try to get to the point what is the best way to manage serialisation for them (I use JMS Serializer) at different level depends on an attribute.
My case:
I have company object, with plenty of attributes that is used itself as API resource [list and details groups].
Now I created an object called card, where I refer to the related company, with some additional data, like score etc. But also I want to store some other companies suggestion, as it is part of the card as well. This is how the class looks like:
class Card
{
/**
* #Serializer\Groups({"details"})
*/
protected $company;
/**
* #Serializer\Groups({"details"})
*/
protected $score;
/**
* #Serializer\Groups({"details"})
*/
protected $type;
/**
* #Serializer\Groups({"details"})
*/
protected $suggestedCards;
}
where $company in a Company object, and $suggestedCompanies is an collection of Company objects. Company object has also groups configured, so all data I want are collected.
With that code, both $company and $suggestedCompanies contains all data that is allowed for details group, but my expectations are to display only some part of data (like id, name, city, so, not ID only) for suggestions.
The problem that I found is that even if I try to create different groups for those properties (suggested_companies), they will still appear with all data from details, as I still use this group to get primary company data.
I solved it in a way, that for suggestedCompanies I had created a separate object, where I pass all required fields in the constructor, so I can set own serialisation rules for that.
It works pretty OK, I'm just wondering is there maybe a better solution for this problem.
Related
If you have a set of association mapped entities in Doctrine, sometimes you might want to retrieve those entities without its mapped associations being fetched and slowing the query down.
For example I have a set of entities which are association mapped in a chain of linked database tables. They are all OnetoMany associations and act as a hierarchy of prices in matrices on product pages. They can be represented as so:
SitePage->SiteMatrix->SiteItems->SiteItemPrices.
The associated mapping works perfectly, and when I use the findBy method to get the root SitePage object it contains arrays which represent the mapped entities down the chain. In other words the SitePage object contains all matrices, which contains all items which contains all prices. So far so good.
My problem is that every time I get a list of pages on my site, doctrine is walking the entire association mapping tree and returning me with the entire datatabase which is very slow. Sometime I want to just get my SitePage entity by ID and not contain all the mapped associations.
I have looked into lazy and extra lazy loading of associations but they only seem to affect certain functions, and not findBy etc. The official documentation is far from helpful:
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html
Other similar questions on Stack Overflow have gone unanswered:
Doctrine 2 Association Mapping Overhead?
Is there an easy way to fetch an entity without its mapped associations? The easiest way I can see currently is to create two entities for each database table, one with association mapping and one without for use in the separate situations where they are required. It seems odd to me that you cannot simply fetch an entity and specify whether you want to link to it to other entities or fetch it by itself.
Thanks for any information on the subject.
The JMSSerializer exclusion strategies can help you.
First, exclude all properties by default :
// ...
use JMS\Serializer\Annotation as JMS;
/**
* #ORM\Entity
* #JMS\ExclusionPolicy("all")
*/
class Foo
Then, choose to exclude or expose your properties :
/**
* #ORM\Column(type="string")
* #JMS\Expose
*/
protected $name;
Also, for your associations, you can use the MaxDepth to restrict the associated entries of your results. Example :
// Entity
/** #MaxDepth(1) */
private $selfAssociatedEntries;
// Controller
use JMS\Serializer\SerializationContext;
$serializer->serialize($data, 'json', SerializationContext::create()->enableMaxDepthChecks());
Like this, your entity will contains the $selfAssociatedEntries serialised, but the $selfAssociatedEntries doesn't have the $selfAssociationEntries property.
The serialisation is restricted to the first parent object.
Groups are a powerful feature that allows you to expose properties for some actions and exclude them for another :
/**
* #ORM\Column(type="string", length=255, nullable=true, unique=false)
* #JMS\Groups({"default"}) // Set the group to expose the property
*/
protected $name;
In your controller, set the group used for serialisation :
// Property 'name' is exposed
$serializer->serialize($data, 'json', SerializationContext::create()->setGroups(array('default')));
For more informations, look at Exclusion Strategies chapter of the documentation.
I have a class Employee extends Person.
Over time an already existing Person can become an Employee of the company.
I know it's a bad practice to either change the discriminator type using raw SQL or typecasting with obscure PECL extensions.
But is there a design pattern that manages these kind of complication?
Is the only way to clone all the attributes and relationships (Person has many relationships to other entities) to achieve this? How would I go about cloning the existing person with all its relationships?
Since Person and Employee are two different "things", you should create an Employee entity with a One-to-One relationship with the Person entity. This implies that an Employee is always a Person, but a Person isn't necessarily an Employee.
class Employee
{
/**
* #ORM\OneToOne(targetEntity="Acme\PersonBundle\Entity\Person", cascade={"persist"})
* #ORM\JoinColumn(referencedColumnName="id", nullable=true)
*/
protected $person;
}
This not only allows you to keep the Person entity with all its existing properties, preventing from having to clone data (which is never a good idea), but most importantly separates the two object types.
As #Zeljko mentioned, you could create a type field field, but I believe that my approach of normalizing the data is a lot cleaner. If you need multiple types of employees, then you could create an EmployeeType table and then have a EmployeeType_ID in the Employee entity. This method is much cleaner than using constants, which tend to get messy and hard to maintain.
Cloning data is bad idea. Remember: if you are duplicating something, you are doing it wrong :)
I think the best solution is to not use inheritance at all. Just create new column in Person entity called eg. type which would be a constant.
In that entity, put something like this:
class Person
{
const TYPE_USER = 0 ;
const TYPE_EMPLOYEE = 1 ;
const TYPE_BIG_FAT_BOSS = 2 ;
...
/**
* #ORM\Column(type="integer")
*/
protected $type = self::TYPE_USER ;
This way you only need to update type column and have better functionality.
I was planning on creating my application and use an ORM for the models, but the thing is, there's a part of the database which uses Entity-Attribute-Value Tables.
I pretty liked Doctrine ORM but I don't know if it is possible to maybe create classes that would look like any ordinary doctrine entity, when the table actually hooked up to is of EAV style.
Would it be possible to use Doctrine on this, and if so, how?
definitely possible:
Have relationships like this:
Object (one to many) -> AttributeValue -> Many to One -> AttributeType
In view of EAV it seems to be obvious how to build a relation between entity and attribute using doctrine. In the most complicated case we deal with a Many to Many relation.
So lets say we want to map an attribute Name to an entity User. Assuming a user has exactly one name and each name belongs to exactly one user this link can be archived using One to One relation
But how to model the relation between attribute and value? The problem is that values can be of different types or even need different numbers of fields in order to save their information.
Consider the attributes name and phone_number. While a name might be represented by a string, an integer could be needed for the phone number. Or it is even necessary to not only the number but also the area code in a separate filed.
Because EAV requires very flexible value representation, it is not possible to store all of them within the same field in a database table (disregard blobs, data serialization an the like). Therefore most EAV implementations using different tables representing different value types.
In order to reach such flexibility, doctrine features Inheritance Mapping. It basically allows you to extend doctrine entities. Doing so you specify a discriminator for each sub-type of your entity:
/**
* #Entity
* #InheritanceType("JOINED")
* #DiscriminatorColumn(name="value_type", type="string")
* #DiscriminatorMap({"name" = "Name", "phone" = "PhoneNumber"})
*/
class Value
{
// ...
}
/** #Entity */
class Name extends Value
{
// ...
}
/** #Entity */
class PhoneNumber extends Value
{
// ...
}
The Value class provides common implementation for all values, i.e. an id. Each subclass (i.e. Name and PhoneNumber) extend those common values by their specific ones, for example additional fields.
The #DiscriminatorColumn defines a column in the parent relation which stores the type of the value.
The #DiscriminatorMap is used by doctrine to map the type from the #DiscriminatorColumn to one of those classes.
The relation between attribute and value can be specified to the parent class. Calling values from the attribute then will fetch all types of values which can be filtered (and dealt with) during runtime using for example instanceof.
I am creating an application with guests, venues, events and guest-categories in symfony2 with doctrine. Being in touch for 3 weeks only with symfony2, I have accomplished to create the schema tables for all entities in sql, their between dependencies and crud for each one. My problem is more theoretical than practical.
I will create a bundle for security (from the Security bundle in the book or FOSUserBundle). Then I will set up three roles:
Administrator
Organiser
Guest
Each guest will have its own profile and each organiser will be "attached" to his guests - in simple words, the Organizer X will only communicate and view Guest X-1, X-2 etc. - he won't be able to view Y-1. It might seem very simple and funny to you that I am asking a question like that but... I am self taught :)
The question is: How can I attach an event to an organiser and its guests? The simplest method I can imagine is : set a unique token (or any self generated code) for every guest, event, category, event and join the tables.
If guest John with token=vsfv2435r3frwf24t5grf has any events/categories/venues
find the same token in the event or category or venue and assign permissions.
Does it sound logical or foolish ?
You need to look at doctrine association mapping (one to many, many to many, etc.. )
http://docs.doctrine-project.org/projects/doctrine-orm/en/2.0.x/reference/association-mapping.html
They are quite easy to set up (you can do it in Symfony with a annotations or in your yml schema file) and will let you do the kind of things you are describing easily, take a look at the Symfony documentation for databases for more info
http://symfony.com/doc/current/book/doctrine.html
You could just create cross table for Organiser and Guest indicating which event they do attend / organise. Each Guest would also have have cross table to Organiser. This way you could easily assign organisers to guests and filter by it (I'm assuming guests could attend multiple events etc).
In Doctrine world it'd look like this:
Event
...
/**
* #ORM\ManyToMany(targetEntity="Organiser", inversedBy="events")
*/
protected $organisers;
/**
* #ORM\ManyToMany(targetEntity="Guest", inversedBy="events")
*/
protected $guests;
Organiser
...
/**
* #ORM\ManyToMany(targetEntity="Event", mappedBy="organisers")
*/
protected $events;
/**
* #ORM\ManyToMany(targetEntity="Guest", mappedBy="organisers")
*/
protected $guests;
Guest
...
/**
* #ORM\ManyToMany(targetEntity="Event", mappedBy="guests")
*/
protected $events;
/**
* #ORM\ManyToMany(targetEntity="Organiser", inversedBy="guests")
*/
protected $organisers;
How would I apply a custom query to a Doctrine entity in Symfony2, using just annotations?
For example:
/**
* #ORM\Table(name="some_object")
* #ORM\Entity()
*/
class SomeObject
{
/**
* #ORM\SomeSpecialQuery()
*/
private $someSpecialResult;
}
$someSpecialResult should contain the results of the special query when used. The attribute should be container aware preferably so that existing repository functions can be called if desired. A typical query could be SELECT x FROM SomeBundle:EntityName x WHERE x.someOtherValue = null LIMIT 5.
My question has come around because I have an entity that I would like to always have access to certain related information (which is much faster to perform as a custom DQL query).
As an added bonus, it would be great if it could be lazy-loaded? Obviously, if there's a better way to do this I am happy to hear it! Thanks.