Doctrine: Custom repository method with inheritable entity - symfony

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.

Related

Doctrine lifecycleCallbacks strange behaviour

I have defined lifecycleCallbacks in yaml as follows:
lifecycleCallbacks:
prePersist: [setCreatedAtValue]
preUpdate: [setUpdatedAtValue]
The above has generated entities with the respective functions as follows:
/**
* #ORM\PrePersist
*/
public function setCreatedAtValue()
{
if($this->created_at == null)
{
$this->created_at = new \DateTime();
}
}
Which looks all fine, right? However, when I try to open the sonata admin page, I get the following error
[Semantical Error] The annotation "#ORM\PrePersist" in method AppBundle\Entity\Article::setCreatedAtValue() was never imported. Did you maybe forget to add a "use" statement for this annotation?
I have never encountered this before and a bit confused about what to do. I am using symfony 2.7.6, Doctrine ORM version 2.5.1, Sonata Admin 2.3.7
Any help will be greatly appreciated
Since you defined your callbacks using yaml, you donĀ“t need to define them again using annotations. Just remove the comments with the #ORM\PrePersist block before the function and everything will be fine.
If you wanted to use annotations to define your doctrine properties, you would need to import them before you can use them. To do so you would need to add this line at the beginning of your file:
use Doctrine\ORM\Mapping as ORM;
Same issue came with me.
In my case everything worked well until I did not Serialize my object in JsonResponse.
So problem was that previously I was not using that entity class (which was giving error) for sending JsonResponse, as soon as I tried to prepare JsonResponse containing that class, JsonResponse failed to serialize my class as it hadn't implemented Serializable interface.
This will happen if you will fetch objects with methods like findAll or findBy which returns Entity objects instead of php array.
So I just skipped those native methods and write doctrine query for fetching data.
You can also implement Serializable interface.

Symfony PageEntity->getByPath() or PageController->getByPath()?

I would like to return the page Entity from the method: getByPath($path). I just would like to know where this method should be in the script. Inside the controller or inside the entity class?
In my opinion the entity "Page" shouldn't have a function called "getByPath()" since an entity should only contain database information of one entity, which can be get or set by getters and setters. And this "getByPath" function is not just a getter or setter it requires me to run the entitymanager within the entity. Am I right?
So am I right that I should make a PageController and create the "getByPath()" (which will return the page object) function there? Or would anyone create that function inside the entity class?
I would like to know what the nicest way is to accomplish this.
Thanks in advance.
You should put that function inside a custom repository for the Page entity
While the Entities are the objects you are storing, the Repository is the class that provides methods to access/load those objects, eg when you call $em->getRepository('Entities\Page')->find($page_id);, you call the find() method on your Page repository and it's its job to find it for you.
Doctrine provides a default repository for each entity (with the various find*() methods, ...), but you can provide a custom one where you can add your own method, such as getByPath().
Symfony 2 - Database and Doctrine - Custom Repository Classes
Doctrine 2 - Custom repository

Add a custom function in a vendor repository

I'm using CCDNForum's ForumBundle (the bundle itself is not relevant, my question isn't related to this bundle specifically but is more general) in my Symfony website, and I want to customize it, so naturally I created a AcmeForumBundle whose parent is CCDNForumForumBundle. The only thing I want to change is to add a new custom function in let say the CategoryRepository associated to the Category entity.
So I created a CategoryRepository.php in my AcmeForumBundle extending the CategoryRepository.php of CCDNForum, and this is not sufficient because the default repository of the Category entity is CCDNForum's CategoryRepository.
The next thing I did was to create a new AcmeForumBundle Category entity extending CCDNForum's Category entity, changing of course the default associated repository
namespace Acme\ForumBundle\Entity;
use CCDNForum\ForumBundle\Entity\Category as BaseCategory;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection as ArrayCollection;
/**
* #ORM\Entity
* #ORM\Entity(repositoryClass="Acme\ForumBundle\Repository\CategoryRepository")
* #ORM\Table(name="CC_Forum_Category")
*/
class Category extends BaseCategory
{
}
I added the #ORM\Table line because I had an SQL error "acme_category" table not found. The class is of course empty as I have nothing to change in the entity.
Using this code everything works fine, I added my custom function in the CategoryRepository without problem, but the problem comes when I want to update the database schema. I have the error when running php app/console doctrine:schema:update --dump-sql
[Doctrine\DBAL\Schema\SchemaException]
The table with name 'acme.cc_forum_category' already exists.
which I can understand since CCDNForum's Category entity and my Acme Category entity are using the same table (the C_Forum_Category table).
My question is: am I doing all this right ? Isn't there a simpler way to add a custom function in a vendor repository ?
Thanks!

With Symfony 2.2 and Doctrine 2.2.* I can't save manyToMany relations with cascade: persist

I try to use Doctrine casecade feature tu automagicaly save relations between two entities, and it does'nt seem to work.
I've made a demo here : https://github.com/asakurayoh/demo_bug_doctrine
So I use the doctrine fixture to make my demo.
you need to create de database (app/console doctrine:database:create), migrate the tables (app/console doctrine:migrations:migrate) and then, load the fixtures (app/console doctrine:fixtures:load). The third fixture (src/Demo/MyBundle/DataFixtures/ORM/TagsNewsFixtures.php) is adding all tags entities to all the news. And if you go to the database, you will see that no relation was save in the news_tag table... I think my relation are well defined in my mapping (Resources/config/doctrine/News.orm.yml and Tag.orm.yml) and the cascade property is set.
Someone can find the problem with this code? I search everywhere (stackoverflow too) and I've done everythings everyone said... it should work...
Thanks to save my life (and my entities relations, ha!)
AsakuraYoh
The problem is in fixtures loading order - TagNewsFixtures is loaded first, therefore no tag nor news are in database at that time. Try forcing load order using ordere
namespace Acme\HelloBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
class LoadData extends AbstractFixture implements OrderedFixtureInterface
{
public function load(ObjectManager $manager)
{
// ...
}
public function getOrder()
{
return 1; // the order in which fixtures will be loaded
}
}
I found the problem.
The "joinTable" preperty need to be on the News side and news use the "inversedBy" property, no the MappedBy (that's the tag). So it work. And to add news to a tag (do the inverse, then), we need to specify in the Tag entity to add the tag to the news... I don't understand why Doctrine doesn't do that by default... weird...

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

Resources