Symfony2 getters, setters, add and remove - symfony

I'm getting really confused by entity properties and what they mean. I understand that get and set allow the application to interact with protected and private properties, but what about add and remove?
When running the command
php app/console doctrine:generate:entities bundle:entity
Sometimes it will generate getters and setters and other times it will generate add and remove properties (and usually a get method as well). I've noticed that it also sometimes depends on the relationship with other entities (i.e. OnetoMany), but not always :-S
Nowhere in the Symfony documentation that I can find is this addressed, and it's causing me to see the message "Neither the property "x" nor one of the methods exist and have public access" way too often. Can anybody provide a succinct explanation of this?

add and remove are used to deal with collections. If for example your Entity (Let's say A) contains a collection of B elements, then the command will provide an addB() and a removeB() public methods to help you add and remove elements from your collection. It'll also provide a getter which returns the whole collection.
The command generates methods based on the type of attributes you're working with (ArrayCollection, string, ...)

For xxxToMany associations, Doctrine will generate a "adder" and a "remover" instead of a plain setter. The idea is to easily add and/or remove a single object from the collection without needing to pass around the entire collection everytime to the setter.
Note though that these generated methods are an implementation detail you are free to revise. If you prefer a single setter method for example, feel free to implement that one yourself.
I personally don't rely on the accessor generation of Doctrine anymore. Doing it manually allows greater control of your entity's API, and is also quite easy in an IDE like Netbeans or PHPStorm.

Related

Adding a callback when reading from an object in Twig

Let's say I have a basic entity called Entity which is mapped to a database table. This entity has got two properties: propertyA and propertyB.
One particularity of this entity is, although we may store whatever we want in these properties, when using the value of propertyB on a Twig template with entity.propertyB we want to systematically truncate the value to 100 characters.
Now, this is perfectly doable in several ways:
Truncate the value directly in the getPropertyB() method;
Register a Twig extension and create a dedicated filter;
Add a lifecycle callback on the entity to truncate the value before the object is actually created.
As this is strictly a display rule, and not a business rule on our entity, the second solution seems to be the best IMHO. However, it demands we apply the filter every time we need to use the value of propertyB in a template. Should an unaware developer come by, the value may not be truncated.
So my question is: is there a way to register some kind of callback, strictly restricted to the view model wrapping our entity, which would allow us to apply some filters on the fly on some of its properties ?
Since you never need to access anything beyond 100 characters, you can truncate the property in its setter. This doesn't really pollute Entity code, because this is some logic inherent to it.

Symfony2: best approach to use business (repository) logic in entity or controller

I'm having a design issue in my project, related to where put some business logic.
I have three entities, Event, TicketOrder and Ticket. One Event has a lot of TicketOrders and one TicketOrder has a lot of Tickets.
In my template, I have to show how many tickets an Event has. I've thinking of the best approach to achieve this and didn't get a good solution. I've tried this:
1) Create a private member 'ticketsCount' in Event entity, with setTicketsCount and getTicketsCount method. Create a 'loadTicketsCount' method with a LifeCycleCallback 'PostLoad', to access the TicketRepository method 'findByEvent'. This was impossible because I can't access repository in an entity class.
2) In the action that will be used to display the Event, I can access Ticket Repository and set event 'ticketsCount' property manually. I don't know if it is a good approach because if my action is listing a lot of events I'll have to loop trough all events and make a repository call to each of then.
I really don't know the best approach to achieve this and will really appreciate if someone can help me.
Thanks! ;)
When you use findAll, findBy or findBy* methods of doctrine entity repository, a simple php array is returned containing the entity objects.
The array class implements countable interface. So using twigs length filter
{{ ticketOrder.tickets|length }}
you perform a simple php count() on the array.
Actually it makes now sense to perform a count query, because you already have the result in memory. So it seems more efficient to count the result and retrieve it from memory, because when you access associations they are completely loaded into memory.
However associations between entities can get pretty large. So imagine you have associations with hundred thousands of entities. You won't those entites to be loaded all together and kept in memory all the time. So in Doctrine 2.1 you can annotate an association as Extra Lazy. If you do so in your case a count query is performed when you call the above twig filter. But the result is not kept in memory.
http://docs.doctrine-project.org/en/2.0.x/tutorials/extra-lazy-associations.html
According to your latest comment:
I can imagine one way to do this. In a template you can call a controller's action with the render statement like
{% render YourMainBundle:getTickets with { 'event_id' : event.id } %}
and in this action you can call a query that looks for all tickets associated to the certain event. This action has to return html, e.g. an template filled with data.

use generated entities as "base class" in sf2/doctrine2

How can I tell Doctrine2 Entity Manager to use an inherited class by default, instead of the generated one?
In my new sf2 app, I generated all entities from the existing database to /src/Package/Bundle/DataBundle/Entity - of course I'd like to have generated forms as well, so I need for my foreign-key relations __toString() methods, which I don't want to put into those generated files because they get overwritten (yes they also get backuped in an extra file, so my changes aren't lost, but manually merging files is not what I want).
So I added new classes to /src/Package/Bundle/DataBundle/Model inheriting all from the entities, but with a __toString() method and surely some other tweaks in the future as well. But now, when I call $entity = $em->getRepository('PackageDataBundle:Customer')->find($id); - I get an instance of /src/Package/Bundle/DataBundle/Entity/Customer instead of /src/Package/Bundle/DataBundle/Model/Customer ..
I'd like to have the behaviour from sf1, where all custom work is done in the inherited classes and the generated ones are the "base" Classes, which can be updated any time on a schema update and aren't touched otherwise..
I hope there is some configuration for this..
Maybe as a bonus, I'd like to have my naming convention turned around: to have Model as the "abstract" generated one and Entity as the actual used one
Thanks.
I'd like to have the behaviour from sf1, where all custom work is done in the inherited classes and the generated ones are the "base" Classes, which can be updated any time on a schema update and aren't touched otherwise..
For purposes you described you should use Propel ORM - it was so in Symfony-1.4, and became even more flexible for Symfony-2.0 with Propel 1.6. It is well documented, easily installed and naturally used within Symfony-2.0
Perhaps I misunderstood your problem, but going from Propel to Doctrine i was also disappointed that there is no way to keep that "dirty" auto-generated code out of my logic.
I do not know how much it is now important to you, but I currently writing a simple bundle that implements such generation style.
$ app/console doctrine:generate:entities СompanySomeBundle --propel-style=true
Generating entities for bundle "СompanySomeBundle"
> backing up User.php to User.php~
> generating Сompany\SomeBundle\Entity\Base\User
> generating Сompany\SomeBundle\Entity\User
https://github.com/madesst/MadesstDoctrineGenerationBundle
PS: Sorry for my english =/

Keep a history of values for specific properties of EF entities

I have a requirement to keep a history of values of some fields in an EF4 ASP.NET MVC3 application. This just needs to be a log file of sorts, log the user, datetime, tablename, fieldname, oldvalue, newvalue.
Although it would be pretty easy to code this in various save routines, I'm wondering if I can get global coverage by wiring it into some sort of dataannotation, so that I can perhaps declare
[KeepHistory()]
public string Surname { get; set; }
in my partial class (I'm using POCO but generated from a T4 template).
So Questions
1) Is this a bad idea ? I'm basically proposing to side-effect changes to an entity, not directly referenced by the code, as a result of an annotation.
2) Can it be done ? Will I have access to the right context to tie up with my unit of work so that changes get saved or dropped with the context save?
3) Is there a better way?
4) If you suggest I do this, any pointers would be appreciated - everything I've read is for validation, which may not be the best starting point.
Actually, validation might be a good starting point. Since an attribute does not know about which property or class it was assigned to, but a validation-attribute gets called by the validation framework with all the necessary informátion. If you implement the System.ComponentModel.DataAnnotations.ValidationAttribute class you can override the IsValid(object, ValidationContext) method, which gives you the actual value of the property, the name of the property and the container.
This might take a lot of work, since you need to get to the currently logged-in user etc. I'm guessing that the .NET implementation provides some sort of caching for the specific attributes on an entity type, which would be a pain to implement by yourself.
Another way, would be to use the ObjectStateManager exposed by your EF ObjectContext, which can provide you with the ObjectStateEntry-objects for all entities of a given state. See the
ObjectStateManager.GetObjectStateEntries(EntityState) method, for more information about how to call it (and when). The ObjectStateEntry actually contains a record of the original and current-values, which can be compared to find any changes made within the lifetime of the current ObjectContext.
You might consider using the ObjectStateManager to inject your custom logging behavior, while this behavior decides based on property-attributes which change should be logged.

Why does Flex's ArrayCollection's Contain method look at memory reference?

When using .contains() on an ArrayCollection in Flex, it will always look at the memory reference. It does not appear to look at an .equals() method or .toString() method or anything overridable. Instead, I need to loop through the ArrayCollection every time and check each individual item until I find what I'm looking for.
Does anyone know why Flex/ActionScript was made this way? Why not provide a way from people to use the contains() method the way they want?
Couldn't you just extend ArrayCollection and override the contains() method? Alternatively you can paste the source for ArrayCollection into an "mx/collections" package in your project and modify the source; this "monkey-patching technique" will override the behavior throughout your entire project. However I would be extremely cautious about changing ArrayCollection in that manner: since it's used all over the place in the Flex APIs there is a good chance you'll start breaking other components in the framework.
The contains() method searches by reference, correct (I believe even for primitives), so if you're trying to find a string or an int in an ArrayCollection, you'll have to do the searching yourself, by some variation of looping or searching. I don't think any of us could tell you why there isn't, say, an optional parameter on that method indicating whether to search by ref or by val, though; so it goes, as they say.
But I'd definitely warn you off monkey-patching the framework code -- that's just asking for trouble. :)
Well, it seems like the ArrayCollection doesn't actually look directly at memory, but only as a last resort. It will attempt to find a Unique ID (UID) for the object. If the UID doesn't exist, it will create one for it using the UIDUtil.as.
You can get around this whole default UID stuff by having your object implement the IUID interface and providing your own UID for the object. The ArrayCollection will look at the UID you provide it.
I would suggest a simple:
in_array($haystack, $arrayCollection->toArray());

Resources