While working on a Symfony project recently, I ran into a rather frustrating bug that required some delving into the depths of the Symfony2 framework and we decided to give it the title; The Prouse of Incorrect Association Names.
The Prouse of Incorrect Association Names Problem
Our project involved a Doctrine entity that had a field called price . At some point, it was decided that this field should become an association – that is to say that the entity could now have multiple prices as opposed to just one. The appropriate change was made to the mapping definition file, the entity classes were modified and the schema was updated. All went to plan.
However, upon submission of the Symfony Form to persist a new instance of this particular entity into the database, a rather odd exception was being thrown:
Neither the property "price" nor one of the methods "addProuse()", "setPrice()", "__set()" or "__call()" exist and have public access in class "MagmaOurBundleEntitySomeOwningEntity".
The part of this message that really got us scratching our heads was
addProuse(). To a developer, this kind of word in an error message screams “typo! ” – so that’s where we started. A search for
ouse to see if one of our entity classes or mapping definitions had a typo in the collection name frustratingly turned up nothing.
With our go-to culprits all checked out, we turned to XDebug. For those who haven’t yet had a go with XDebug, I’d strongly recommend taking a look at Derick Rethans’ talk on getting the most out of XDebug.
The approach was to look at the stack trace for the exception and place some breakpoints inside the methods leading up to the exception being thrown to find out where the erroneous
addProuse string was coming from. With a project using a large framework such as Symfony, this is really a better approach than stepping into methods from the start of execution. Even with step-into filters, this can be an extremely slow process.
XDebug revealled a little class within Symfony called
SymfonyComponentPropertyAccessStringUtil. This class is within the PropertyAccess Symfony Component, which is responsible for resolving the string paths to properties within an object graph.
One of the methods exposed by this class is called
singularify . The docblock specifies that it should “Return the singular form of a word.“. Inside the class is a static variable called
$pluralMap that contains a rather extensive list of the odd rules in the English language that map singular nouns to plural nouns.
One of these mappings is ice to ouse (as in mice to mouse). Aha! Ran through this function, price would become… “
The Prouse of Incorrect Association Names Lesson
The fact that this method is being called on our association name told us that our associations should be named plurally – prices not price.
However, we couldn’t help thinking that this class might be a little big for its’ boots. English is an extremely difficult language to codify as a set of rules like this, and this naive implementation is bound to make mistakes that will leave developers scratching their heads and searching for typos. A few feasible association names would be scrambled by this method – “
series” for example gets
singularify‘d to “
It’s also an affront to internationalisation – while code is usually in English, entity and association names aren’t guaranteed to be. What if the domain that the project is targeting handles uniquely Polish or Italian specialised entities, for which these English rules aren’t applicable? The
StringUtil class can’t be injected or swapped out particularly easily either because of its use of static properties and methods.
We’ll be considering making a pull request to the Symfony project to fix these issues, or at the very least make the error message more explanatory!