Doctrine: Allow extending STI entities from within 3rd party code - symfony

Background
I've got an entity A with single table inheritance set up and class Foo extending it like so:
/**
* #ORM\Entity()
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="type", type="string")
* #ORM\DiscriminatorMap(
* "foo" = Foo
* )
*/
abstract class A
{
// ...
}
/**
* #ORM\Entity()
*/
class Foo extends A
{
// ...
}
This works with no problems. However, I need to be able to let 3rd party code extend A as well. Problem: I don't know the discriminator mappings that would be needed to wire this up beforehand as this is a property of the base class.
My current solution
I'm in Symfony land, so I can make use of the dependency injection container and decorate the annotation_reader* (that processes the discriminator map class annotations) with my own one.
This reader delegates most of the calls to the original one but can extend found discriminator map annotations. It does so based on an 'extension registry' which itself is filled by a compiler pass looking for a special service tag. Anyone wanting to extend the core entity A with Bar can now tag Bar with this service tag (that also includes the identifier for the mapping; the column type in this example).
*) this will probably also work within the loadClassMetadata event - which would be preferable instead of decorating the reader
/**
* #ORM\Entity()
* #DiscriminatorMapExtension("bar")
*/
class Bar extends A
{
// ...
}
(I'm using service annotations in the above example to be able to write the tags directly beneath the entity annotation. But how exactly the classes will be registered in the end doesn't matter right now. I'm more interested in the general concept.)
So if it's working, what's the problem?
I'd really love to hear some feedback from experienced Doctrine devs on this approach, especially:
Is there a better way to achieve a 'shared entity' across bundles?
Why could this pattern be a problem?
Thanks a lot in advance!

Related

Doctrine: Custom repository method with inheritable entity

I have a base entity class called User, which has 3 possible different child-classes Employee, Customer and Student. This is implemented with a single table inheritance.
The base class User has two fields called nameand prename, which all the child classes inherit.
Now my problem: I have a custom repository called UserRepository with the method findByFullTextSearch(string $searchterm): User[] in which I implemented a somewhat Full-Text-Search on those two fields to fit my needs (unfortunately, I do not have the possibility to use DoctrineExtensions for this project to implement the MySQL function FIND_IN_SET or an actual Full-Text-Search).
Is there any possibility for me to only write this method once in UserRepository, so when I use $doctrine->getRepository(Student::class)->findByFullTextSearch('john doe'); I only get results fitting to the Repository i called it from?
My workarounds so far:
Implement all repository classes, pass get_called_class() or $this->_entityName to parent class.
Call the base repository class and pass the child entity class as parameter to findByFullTextSearch
Here I found a related question, but not quite the same.
Is there a better solution to this? Thanks in advance.
I just found the solution by simply adding the base class as repository for each child entity:
<?php
namespace UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* UserBundle\Entity\Customer.
*
* #ORM\Entity(repositoryClass="UserBundle\Repository\UserRepository")
*/
class Customer extends User
{
// ...
This way I wont need empty child repositories just for doctrine to find a possible entry point.

Using htmlpurifier with annotations in symfony 2.8

I would like to use the htmlpurifier not as a form data transformer, but rather as an annotation in the entities property.
The problem: In the Symfony 2.8 project we use a lot of different types of forms, some using symfonies formbuilder, some via sonata-admin, some using twig and ajax etc ..
So using a transformer and a custom field type is not the way to go.
Also, editing every single create or update action would be overkill.
I've been playing with the idea of an event listener on prePersist, but with such a large amount of different entities in our project we would most likely incur a performance hit if this were to be called on every persist() action. Limiting it to only specific object types would also be painstaking.
Is it possible to use the htmlpurifier bundle as an annotation only on the properties that matter?
eg:
/**
* #var string
*
* #ORM\Column(name="uprename", length=20, nullable=true)
* #htmlpurifier('default')
*/
private $firstName;
Where default would be the default configuration.
bundle: https://github.com/Exercise/HTMLPurifierBundle

How to auto complete methods from Symfony 2 DI in netbeans

I am starting to develop with symfony 2 and it uses a lot dependency injection. I would like to know if is there any way that makes netbeans detect the type of object based on the string and auto complete with their methods?
For example, $this->container->get('doctrine') returns a Doctrine\Bundle\DoctrineBundle\Registry instance. In the container, the key doctrine corresponds to Doctrine\Bundle\DoctrineBundle\Registry.
Something like it, could be useful for zendframework 2 also.
I don't want to create new methods in the controller and nor use /* #var $var Symfony...*/, I would automatic detection.
As far as I know, there's no way for an IDE to detect the type of the object your container returns. My solution is to wrap those calls to the container into private getter functions. IMHO this improves code readability as well – especially, if you do this call more than once per class.
/**
* #return \Doctrine\Bundle\DoctrineBundle\Registry
*/
private function getDoctrine()
{
return $this->container->get('doctrine');
}
The IDE "PhpStorm" permits to suggest "use" declarations.
And this IDE propose specific features for Symfony2 and Drupal !
edited by JetBrains : http://www.jetbrains.com/phpstorm/
Not free but power full enough to reduce time developpement time (and time is money...)
Enjoy : )
phpStorm:
$foobar= $this->get('foobar'); //returns mixed
/* #var \MyNamespace\FooBar $foobar*/
or
$foobar= $this->get('foobar'); //returns mixed
/* #var FooBar $foobar*/
You can do this with eclipse PDT:
$foobar= $this->get('foobar'); //returns mixed
/* #var $foobar \MyNamespace\FooBar*/
( Walk around ) When comes to Symfony services:
Instead of
$doctrine = $this->container->get('doctrine');
use
$doctrine = $this->getDoctrine();
As you can see, Symfony allows you to access most of it services directly from $this variable. NetBeans will know what auto completion to use.
Lets have a look why this works (inside Controller class)
It is possible because Controller class imports Registry class with USE statement,
use Doctrine\Bundle\DoctrineBundle\Registry;
and then in method comment annotation it declares the returning object type with
/*
* #return Registry
*/
If you call $this->container->get('doctrine'); directly then auto completion will be get omitted and you will have to use whats below.
( Answer ) No magic auto completion works so far. Use Php Storm (it does what you request). For those who pick to stick with NetBeans you need to use manual annotation like in example below:
We can point NetBeans to a class it should be using for auto completion.
1) In terminal from project directory search for service you want to import:
php bin/console debug:container
If you know what you looking for use this instead:
php bin/console d:container | grep doctrine
...
doctrine --------------------------------------------------------
Doctrine\Bundle\DoctrineBundle\Registry
...
2) If this is not a service use get_class() PHP build in function to get class name of the object it particular variable. Or use reflection class. It's up to you.
3) Once you know the class name declare USE statement for better readability
use Doctrine\Bundle\DoctrineBundle\Registry;
4) Now wen we know what is the class name of the object instance in particular variable we are ready to inform NetBeans about what we know by using comment annotations so that it can enable auto completion.
/**
* #var $doctrine Registry
*/
$doctrine = $this->container->get('doctrine');
Now auto completion is enabled. Type
$doctrine->|
then press Ctrl+Space. See the image below:

How can I use Doctrine Annotations to change a column name in an entity subclass?

I'm using FOSUserBundle with a new Symfony project that has to work with an existing schema. My user entity extends FOS\UserBundle\Entity\User as the instructions say, but the email column is named "email_addr" instead of "email". Since the parent defines $email I can't re-declare it to attach my annotation.
/**
* #var string $emailAddr
*
* #ORM\Column(name="email_addr", type="text", nullable=false)
*/
protected $email;
The exception I get is:
[Doctrine\ORM\Mapping\MappingException]
Property "email" in "Foo\DataBundle\Entity\User" was already declared, but it must be declared only once
My question is either:
How can a Doctrine2 subclass use an annotation to alter something defined in the parent?
Or how can I override column names in the FOSUserBundle?
I found an answer:
More about Doctrine implementations
If you need to change the mapping (for instance to adapt the field names to a legacy database), the only solution is to write the whole mapping again without inheriting the mapping from the mapped superclass. In such case, your entity should extend directly from FOS\UserBundle\Model\User (and FOS\UserBundle\Model\Group for the group).
It looks like doctrine 2.3 added these features. Unfortunately, SO decided that I had to duplicate information already present on their site to avoid a "trivial answer".
#AssociationOverride and #AttributeOverride in new Doctrine 2.3

Extend Symfony2 White October Admin Bundle Form Fields?

This question is quite specific.
I'm using Symfony2 White October Admin Bundle for generating administration for my application.
I wonder if anyone has experience with this bundle and knows how to modify specific fields added to Admin class. The bundle is not documented very well and I'd like to know, if there are methods for doing this, or does the bundle core need to be extended.
For example I've got a Speaker entity with a field storing the path to one's avatar:
/**
* #var string $picturePath
*
* #ORM\Column(name="picture_path", type="string", length=128, nullable=false)
*/
private $picturePath;
Then in the Admin class I'm adding the field:
protected function configure()
{
...
...
->addFields(array(
...
...
'picturePath'=> array("label" => "Avatar"),
));
}
Is there an easy way to tell the bundle that I'd like this field to be an upload field instead of a text field, and define extra methods to call after submitting the form? (e.g. resize the image, store it, and then just store the image path in the DB)
As I haven't found any solution yet, I had to rape my entities to do that. So I modified the basic setter methods to call other methods inside the entity that do the dirty job. This is not a solution, and I'll still be happy if I could find better answers here.

Resources