Adding a callback when reading from an object in Twig - symfony

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.

Related

Symfony2 getters, setters, add and remove

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.

Multiple "default" properties/methods in a VB6 class?

I am trying to make a replacement VB6 class for the Scripting.Dictionary class from SCRRUN.DLL. Scripting.Dictionary has (among other things) a "Keys" method that returns an array of keys, and a read/write "Item" property that returns the item associated with a key. I am confused about this, because both of them seem to be defaults for the class. That is:
For Each X In MyDict
Is equivalent to:
For Each X In MyDict.Keys
Which to me implies that "Keys" is the default operation for the class, but:
MyDict("MyKey") = "MyValue"
MsgBox MyDict("MyKey")
Is equivalent to:
MyDict.Item("MyKey") = "MyValue"
MsgBox MyDict.Item("MyKey")
Which to me implies that "Item" is the default operation for the class.
I've never before created a VB6 class that had a default operation, so upon realizing this, I thought perhaps I could define multiple default operations as long as they all have different signatures, which they do: Keys is nullary, the Item getter takes a Variant, and the Item setter takes two Variants. But this doesn't seem to be allowed: When I use "Tools/Procedure Attributes" to set the Keys function to be the default, and then I use it to set the Item property to be the default, the IDE complains that a default has already been set.
So I think I'm misunderstanding something fundamental here. What is going on in the Scripting.Dictionary object that makes it able to act as if "Keys" is the default in some contexts, but as if "Item" is the default in others? And whatever it is, can I accomplish the same thing in VB6?
OK, answering my own question: I haven't tried this yet, but I gather that "Item" should be made the default, and that I should add an entirely new function called "NewEnum" that looks something like the following (slightly modified from an example in Francesco Balena's "Programming Microsoft Visual Basic 6.0" book):
Public Function NewEnum() As IUnknown
Set NewEnum = m_Keys.[_NewEnum]
End Function
(where "m_Keys" is a Collection containing the keys), and then use Tools/Procedure Attributes to hide NewEnum and to set its ProcID to -4.
What you are observing is the difference between the default member and a collection enumerator. A COM object (including VB6 classes) can have both.
You can identify the default property of a class by looking in the Object Browser for the tiny blue globe or the words "default member of" in the description (see Contents of the Object Browser). The Object Browser will not identify an enumerator method, but if you look at the class's interface definition using OLE View or TypeLib Browser (free but registration required) it's DispId will be 0xfffffffc or -4.
In your own class, you can mark the default property by setting the Procedure ID to "(default)" in the Procedure Attributes dialog (see Making a Property or Method the Default). You already listed the steps for setting up the collection enumerator in your own answer, but you can find this listed as well in the Programmer's Guide topic Creating Your Own Collection Class: The House of Bricks.
Scripting.Dictionary has a dirty secret:
It does not handle enumeration at all, it returns big ugly Variant arrays and your For Each loops iterate over those.
This is one of the reasons why a Dictionary can actually be far less efficient than a standard VB6 Collection.

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.

Setting field defaults on programmatically created Dexterity items

I have a Dexterity content type based on plone.directives.form.Schema which has a number of form hints for assigning defaults:
#form.default_value(field=ITrial['start_on'])
def default_start_on(data):
return datetime.now()
Some of the defaults are more complex, passing back objects that are themselves instances of Dexterity types. These objects are essential for the main type's setup, which is triggered by various events.
I'm now in the process of testing. Ideally, I'd like to be able to use something like:
item = createContentInContainer(folder, 'ctcc.model.trial', 'item')
That is, I'd like the defaults to be picked up by the item without having to be manually passed into the constructor.
If I was using zope.schema I could use FieldProperty to set up proxies to the schema fields. Is there something equivalent for Dexterity, or perhaps a function for pushing an object through form creation?
Solution: I ended up going with David's option #1, intercepting ObjectCreatedEvent.
#grok.subscribe(ITrial, IObjectCreatedEvent)
def create_trial(trial, event):
if getattr(trial, 'start_on', None) is None:
trial.start_on = default_start_on(None)
It stills feels like I'm replicating part of form behaviour, but at least it's using the same functions that are providing the form defaults.
As you've discovered, the #form.default_value decorator is respected by z3c.form forms, but not when items are created in other ways. You have several options:
Write a handler for the ObjectCreatedEvent for your content type which sets up the necessary default values. This is the simplest approach to implement, but may not work if there are other handlers of this event that need the values in place.
Create your own subclass of Dexterity's generic Item class, and use it instead of Item as the basis for your content type. Then you can customize the __init__ method to set whatever you want. This would require migration of existing content items though, if you already have some.
This is a more complicated option. Replace the factory utility used to construct the content type. createContentInContainer ends up looking for an IFactory utility with a name equal to the factory attribute of the content type's FTI. The default implementation is in plone.dexterity.factory but you could replace it with a different one that does more.

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.

Resources