How to deal with calculated entity attribute - symfony

Let's say there is an entity called Staff.
It has a number of persistent attributes, such as:
- Name
- Experience
- Age
I want to create a "virtual" attribute, that is based on the Experience and Age, called 'Salary'. For example: $salary = ($experience + $age) * 100
But I don't want to persist the Salary attribute. The reason is that I want to let the Salary attribute get's updated automatically whenever the age or experienced values change.
I have two questions regarding this:
Is the Entity file a good place to store the getSalary() function?
How can I make it so that whenever a Staff entity is called, the salary variable will be filled with the salary that is calculated based on age & experience?

Is the Entity file a good place to store the getSalary() function?
Yes, it is.
Not every field in your entity has to be mapped to a database field.
Also, entities can contain methods other than simple getters and setters. IMO as long as those methods operate on the entity fields, they belong to the entity.
How can I make it so that whenever a Staff entity is called, the salary variable will be filled with the salary that is calculated based on age & experience?
You could use one of the Doctrine's lifecycle events, for example the postLoad event, which is called after entity is loaded to the entity manager.
Note, that you don't have to be storing calculation results in a property. Your calculation is simple and it's probably better to define a getter.

Related

Symfony CollectionType update Entities

I can't believe this hasn't come up for other people, but I'm unable to find a solution.
Let's say I have two entity types, A and B with a one-to-many relationship. A has a collection of Bs.
The form for A has a CollectionType for the Bs, with a custom entry_type for B, allow_add and allow_delete set to true. When the form is created/populated/rendered, the Bs' fields are identified by their index in the collection. When the form is posted back, the fields are mapped back onto the B entities according to the index again.
What if the database in the mean time decided to return the Bs in a different order? Then the values get swapped around on the Bs! I can't have that, as other entities will reference the Bs and now they've changed their meaning!
Even if the database doesn't change the order, the same issue appears when a B is deleted: The fields get shifted through the Bs and a different one deleted! (Ok, I'm not a 100% certain this happens, as there's a gap then in the numbering of the posted fields.) I've found this similar question where it does happen when another one is created (Symfony CollectionType regards deletion+creation as a modification of an item), but that sort of drifted from the issue and there's no usable answer.
How do I make sure the form is updating the entities the user actually edited?
I already tried to render the Bs' IDs as a HiddenType, but then the form rightfully complains that the ID has no setter. It would probably force an ID on the wrong B anyways and Doctrine doesn't like that. I suppose I could add the Bs as unmapped and copy the values over to the correct objects manually, but that would defeat a good chunk of Symfony's form system.
I've used CollectionType before, but not for entities that are referenced elsewhere. I would then delete all of the previous entities and create the collection anew from the posted data. But I can't do that now, can I?
Since doctrine 2.1, it's possible to change how associations are indexed. This will allow you to use the id as the collection key (as the field has to be unique):
#OneToMany(targetEntity="B", mappedBy="A", indexBy="id")
You might also need to enable orphanRemoval so that the data is actually removed instead of the relation just set to null.

Symfony2 - extending existing non-abstract entity?

Let's say I have a Setting entity with some fields like IntValue, dateValue, stringValue and some linked entities, like countries (ManyToMany to entity Country), languages (ManyToMany to Language) etc.
Settings are created by users and assigned to specific objects (not important here, but I wanted to clarify).
Now I suddenly need to have UserDefaultSetting, which will be the same, but with additional user field (ManyToOne to User entity).
I tried to extend existing Setting entity class with one more field added. The problem is, as I looked at the schema update SQL, it created new table for the new entity, but without all the tables needed to ORM connections (mostly ManyToMany). Just one table with "scalar" fields.
So previously I've had setting table with int_value, date_value etc. but also setting_country and setting_language tables, linking ManyToMany relations. After creating child entity, Doctrine created only user_default_setting table with int_value, date_value etc. and additionally user_id column, but I can't see any relation/link tables.
I know I should've been do it with abstract base entity class, but at the time I started, I didn't know that and now part of the project is on production (don't look at me like that, I blame the client) and I don't want to change that "base" class now. Can I inherit everything from non-abstract entity class in a way it will work?
UPDATE: everything explained. See Cerad's comment. Thanks!

Symfony2 dynamic relationship with a field

I am building a social website and I am laying out how the feed will work. I want to use the answer here: How to implement the activity stream in a social network and implement the database design mentioned:
id
user_id (int)
activity_type (tinyint)
source_id (int)
parent_id (int)
parent_type (tinyint)
time (datetime but a smaller type like int would be better)
The problem is I don't know how I would map the source_id based off activity_type. If a user registers, I want the source_id to be the user that registered. If someone creates a group the source_id will be the group. I know I can just use simple IDs without keys I just wanted to know if Symfony had some sort of way to do this built in.
If I fetch the feed and the activity_type is user_register I would like to be able to do this to get the source (user) without running an additional query:
$feedEntity->getSource()->getUsername(); //getSource() being the User entity
And if the source_typeis "user_post":
$feedEntity->getSource()->getMessage(); //getSource() being the UserPost entity
I basically just want to find the best way to store this data and make it the fastest.
Not easy to deal with doctrine and i think it cannot achieved 100% automatically
However, the keyword is table inheritance
http://docs.doctrine-project.org/en/2.0.x/reference/inheritance-mapping.html#single-table-inheritance
I think you could achieve your goal by doing something like this :
You create a discriminator map by the type column of the table which tells doctrine to load this entity a UserSource (for example)
This UserSource can be an own entity (can be inherited from a base class if you want) where you can decide to map the source_id column to the real User Entity
You can use instanceof matching against the namespace of the different entities mapped inside your discriminator map to define different behaviours for the different sources

Symfony2 Doctrine Get reference based on attribute

in my Symfony2/Doctrine2 project I have an entity Person which has a birth date. I have another entity Agegroup which stores a name and some more information for a group based on the age in years.
Example:
Person Name: xy Date: 1980-05-06
Agegroup: From: 1 To: 10
Agegroup: From: 11 To: 20
Agegroup: From: 21 To: 30
I want to get the Agegroup which a person currently belongs to, from within the entity (based on current date).
i.e. :$person->getCurrentAgeGroup()
Therefore I would have to access another repository class from within the entity, which is obviously not a good thing to do.
Is there a way to implement this kind of functionality?
I read Using EntityManager inside Doctrine 2.0 entities
which could be a solution to the problem. Unfortunately I didn't find a solution to implementent this. Do I have to inject the service somehow into the entity?
Maybe there are other best practices for this kind of problem?
It is not good style to put the entity manager into your entities. A better approach would be to create a custom AgegroupRepository that has a method getAgegroup(Person $person)
Another approach would be to make the age group a property of Person (with getters and setters), create a custom PersonRepository and modify the find() method to instantiate the correct instance of Agegroup when looking for a person.
Your entity objects should only store data and business rules and should not concern themselves with the storage of the information. This is what repositories are for.

When assigning values to EntityRef ID fields in Linq to Sql, can EntityRef still delay load?

I've got an ASP.NET MVC app that uses Linq to Sql for data access.
Say I have two objects: An Order object that has a foreign key to a Customer object by CustomerID. So, in my Order class, you would see two properties: an int CustomerID field, and an EntityRef member accessible by a Customer property.
When the edits or submits an Order, my MVC app will update the CustomerID field directly of the Order class, instead of updating the Customer property. This saves us from having to fetch a customer record, and I can use the default model binding code to fill the property automatically as long as the submitted form request has a customerID entry.
This works ok, however, later on in some other part of the code--say a business rules portion, some logic will access the Customer property of the Order object. For example:
if (order.Customer.HasPreviousOrders) then ...
Even though the CustomerID field is set, the Customer field is null, so this business rule throws an exception.
I know Linq 2 Sql uses EntityRefs to do delayed loading. My question is: is there a way to trigger the delayed loading on an object's EntityRef if the ID field has been modified?
We have a dynamic rules engine, so I don't have control of what foreign key objects are going to be needed. I'd rather not have to go through all my controllers to set the EntityRef<> values directly.
Thanks for the help.
Ok, no takers. It looks like what I'm trying to do is just not doable--or maybe not a good idea.
I went ahead and implemented code so I am setting the association object property instead of the ID property so the business rules can be processed.

Resources