Symfony2 - Inheritance - repository find method - symfony

I have entities
<?php
namespace Proj\Bundle\MyBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* ClassTop
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Proj\Bundle\MyBundle\Repository\ClassTopRepository")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="aVal", type="string")
* #ORM\DiscriminatorMap({ "ValOne" = "ClassSubOne", "ValTwo" = "ClassSubTwo", "ValThree" = "ClassSubThree" })
*
*/
class ClassTop
{
.....
}
class ClassSubOne extends ClassTop
{
....
}
class ClassSubTwo extends ClassTop
{
....
}
class ClassSubThree extends ClassTop
{
....
}
My problem is when I call the find() (or findOneBy, findAll, etc) method, I have an Oracle error.
$entityManager->getRepository('ProjMyBundle:ClassSubOne')->findAll()
In the query I have
SELECT field, field2 FROM CLASSTOP WHERE aVAL IN ()
The discriminator value "ValOne" is not mapping and not passed in Doctrine.
Any help would be much appreciated.

You need to repository classes for the subclasses not the top one. You treat your subclasses as full defined classes and that's all. Then when you need to query for your entities you don't go for the top entity but the subclasses. I don't know if I made myself clear here!
In your case:
#ORM\Entity(repositoryClass="path\to\your\repository")
The repository line goes for the subclasses not the top superclass. so you do
/**
*
* #ORM\Entity(repositoryClass="path\to\your\ClassSubOneRepository")
*/
class ClassSubOne extends ClassTop
{
...
}
instead of
/**
*
* #ORM\Entity(repositoryClass="Proj\Bundle\MyBundle\Repository\ClassTopRepository")
*/
class ClassTop
{
...
}

Related

API PLATFORM custom identifier in parent class/table

I have entities that make use of Inheritance Mapping Doctrine inheritance. I have a custom identifier that I generate with #ORM\PrePersist(), which is in a trait and this is used in the parent class.
I want to be able to update properties that the child class has, for this reason, I need to run endpoints on the child entity
When I run an item operation, api platform can't find the resource.
PATCH /api/childas/{hash}
NotFoundHttpException
Not Found
api platform, it doesn't recognize hash as identifier. Take the id as your identified, even if it is false and hash is true.
Trait to generate hashes with which I identify the resource
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiProperty;
use Doctrine\ORM\Mapping as ORM;
trait HashableTrait
{
/**
* #ORM\Column(type="string", length=255)
* #ApiProperty(identifier=true)
*/
private $hash;
public function getHash(): ?string
{
return $this->hash;
}
/**
* #ORM\PrePersist()
*/
public function setHash()
{
$this->hash = \sha1(\random_bytes(10));
}
}
Parent class, is the table where the hash will be stored
<?php
namespace App\Entity;
use App\Entity\HashableTrait;
/**
* #ORM\HasLifecycleCallbacks()
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="type", type="integer")
* #ORM\DiscriminatorMap({
* 1 = "App\Entity\ChildA",
* 2 = "App\Entity\ChildB"
* })
*/
class Parent
{
use HashableTrait;
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #ApiProperty(identifier=false)
*/
private $id;
public function getId(): ?int
{
return $this->id;
}
// Properties, setters, getters
}
Child class, on which I want to perform operations, such as updating some property that belongs to this class
<?php
namespace App\Entity;
class ChildA extends Parent
{
// Custom properties for ChildA
}
Config api platform operations for entity Child
App\Entity\ChildA:
collectionOperations:
post: ~
itemOperations:
post: ~
get: ~
patch: ~
delete: ~
I have thought about using data providers, but I keep getting the error.
The error was because both the hash property in the trait and the id property in the parent entity must be accessible from the entity to use.
Doctrine ORM uses reflection class to get information about attributes and their annotations. ReflectionClass::hasProperty obviously does not allow viewing private properties in the parent class.

Symfony Custom Repository class not working

I have a class
/**
* #ORM\Table(name="registration_number")
* #ORM\Entity
* #ORM\Entity(repositoryClass="PNC\MISDashboardBundle\Repositories\RegistrationNumberRepository")
* #ORM\HasLifecycleCallbacks
* #ORM\Entity#EntityListeners({"RegistrationNumberListener"})
*/
class RegistrationNumber {
}
and the repo class
namespace PNC\MISDashboardBundle\Repositories;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\NoResultException;
/**
* RegistrationNumberRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class RegistrationNumberRepository extends EntityRepository {
public function findByTotalMatches($keyword)
{
/* your awesome code block */
return 34;
}
}
and I am calling the method in this way;
$check = $em->getRepository('PNCMISDashboardBundle:RegistrationNumber')
->findTotalMatches(5);
But it says that;
Undefined method 'findTotalMatches'. The method name must start with
either findBy or findOneBy!
I have built lot of other custom repo and works, i don't know that wrongs with this one. has anyone any hint what is wrong with this.
As said in comment,
Change :
/**
* #ORM\Table(name="registration_number")
* #ORM\Entity
* #ORM\Entity(repositoryClass="PNC\MISDashboardBundle\Repositories\RegistrationNumberRepository")
* #ORM\HasLifecycleCallbacks
* #ORM\Entity#EntityListeners({"RegistrationNumberListener"})
*/
class RegistrationNumber {
To :
/**
* #ORM\Table(name="registration_number")
* #ORM\Entity(repositoryClass="PNC\MISDashboardBundle\Repositories\RegistrationNumberRepository")
* #ORM\HasLifecycleCallbacks
* #ORM\EntityListeners({"RegistrationNumberListener"})
*/
class RegistrationNumber {
And it should works.

Symfony2 Relationships using Interfaces results in duplicated table

I'm trying to relate an entity in one bundle with another in another bundle to make the second one independent from the first one, and be able to reuse it.
I'm following this documentation and this StackOverflows answer.
In the reusable bundle I have a Folder, File a that belongs to the folder and and interface like this:
namespace Acme\FolderBundle\Entity;
/**
* #ORM\Entity
*/
class Folder implements FolderInterface
{
// Has many files
}
namespace Acme\FolderBundle\Entity;
interface FolderInterface
{
// no methods here
}
namespace Acme\FolderBundle\Entity;
/**
* #ORM\Entity
*/
class File
{
// Belongs to one folder
}
And on the other bundle just one class:
namespace Acme\NewBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Acme\FolderBundle\Entity\Folder as BaseFolder;
use Acme\FolderBundle\Entity\FolderInterface;
/**
* #ORM\Entity
*/
class Folder extends BaseFolder implements FolderInterface
{
// Has many files
}
And the config.yml's ORM configuration:
orm:
auto_generate_proxy_classes: %kernel.debug%
auto_mapping: true
resolve_target_entities:
Acme\FolderBundle\Entity\FolderInterface: Acme\NewBundle\Entity\Folder
If I try to update my database schema, I get the following error:
[Doctrine\DBAL\Schema\SchemaException]
The table with name 'foldersDatabase.folder' already exists.
To get this working, I have to explicitly change one of the Folder's Entities table:
namespace Acme\FolderBundle\Entity;
/**
* #ORM\Entity
* #ORM\Table(name="distributed_folder")
*/
class Folder implements FolderInterface
{
// Has many files
}
Then, everything works but I get stuck with a table in my database (distributed_folder) that is never used.
Thanks a lot in advance!!
EDIT:
Fixed the annotation in the FolderInterface
You can not make one entity extend another entity this way.
If you want to have an abstract class which contains the fields for two or more subclass entities, you should mark the abstract class as #ORM\MappedSuperclass , and make sure, it will not have the annotation #Entity. While on the subclasses , they each should have #Entity annotation , and #Table annotation with a unique name attribute.
Here is an example :
<?php
namespace Radsphere\MissionBundle\Model\Core\BaseAbstract;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\MappedSuperclass
*
* An abstract class implementation of mission
*/
abstract class AbstractMission implements MissionInterface, IntegratedPluginInterface
{
/**
* #ORM\Id()
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=36, unique=true)
*/
protected $guid;
/**
* #ORM\Column(type="string", length=255)
*/
protected $title;
/**
* #ORM\ManyToMany(targetEntity="MissionTask", cascade={"persist", "remove"})
* #ORM\JoinTable(name="mtm_mission_task",
* joinColumns={#ORM\JoinColumn(name="mission_id", referencedColumnName="id", onDelete="CASCADE")},
* inverseJoinColumns={#ORM\JoinColumn(name="task_id", referencedColumnName="id", onDelete="CASCADE")}
* )
*/
protected $tasks;
/**
* {#inheritDoc}
*/
public function addTask(MissionTaskInterface $missionTask)
{
$this->getTasks()->add($missionTask);
$missionTask->setMission($this);
}
/**
* {#inheritDoc}
*/
public function setTasks(Collection $tasks)
{
/** #var MissionTaskInterface $task */
foreach ($tasks as $task) {
$task->setMission($this);
}
$this->tasks = $tasks;
}
/**
* {#inheritDoc}
*/
public function getTasks()
{
$tasks = $this->tasks;
foreach ($tasks as $task) {
if ($task instanceof MissionTaskInterface) {
if (!$task->getIsEnabled()) {
/** #var $tasks Collection */
$tasks->removeElement($task);
}
}
}
return $tasks;
}
}
and the entity itself:
<?php
namespace Radsphere\MissionBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Radsphere\MissionBundle\Model\Core\BaseAbstract\AbstractMission;
/**
* Mission entity
*
* #ORM\Table(name="mission_bundle_mission", indexes={#ORM\Index(name="guid_idx", columns={"guid"})})
* #ORM\HasLifecycleCallbacks
* #ORM\Entity(repositoryClass="MissionRepository")
*/
class Mission extends AbstractMission
{
/**
* Constructor
*/
public function __construct()
{
$this->tasks = new ArrayCollection();
}
}

Doctrine Class Table Inheritance with Symfony2

I have 2 Symfony bundles. AdminBundle will always be installed. PageBundle may or may not be installed.
I want to define a base Entity called AdminModule (name, controller class, description, enabled), and also a PageModule which simply inherits from AdminModule ( the entities controller will implement a specific interface).
<?php
namespace AdminBundle\Entity;
/**
* Admin Component
*
* #ORM\Entity
* #ORM\Table(name="admin_module")
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="discr", type="string")
* #ORM\DiscriminatorMap({"page" = "\PageBundle\Entity\PageComponent"})
*/
class AdminModule
{
// private vars, getters, setters
}
?>
<?php
namespace PageBundle\Entity;
use AdminBundle\Entity\AdminModule;
/**
* Page Component
*
* #ORM\Entity
* #ORM\Table(name="page_module")
*/
class PageModule extends AdminModule
{
//
}
?>
The issue I have, I think, is that the AdminModule annotation #ORM\DiscriminatorMap({"page" = "\PageBundle\Entity\PageModule"}) requires definition on the AdminBundle - but the PageBundle may not be installed.
I believe must have the wrong type of inheritance structure (?) however I am not clear on what alternative approaches I can take? Thanks for any help or direction :)
you can't do what you're trying to with table inheritance mappings,
because you have to write annotations in the parent class, so the parent class itself ends up being coupled with his children.
what you could use is a mapped superclass (#MappedSuperclass) to extend the actual parent entities from.
all your common properties should then go into the mapped superclass, using its children as actual entities to define different inheritance mappings and associations (association mappings in mapped superclasses are very limited).
so in your specific case you could have such a structure:
/**
* I'm not an actual Entity!
*
* #MappedSuperClass */
Class ModuleSuperClass {}
/**
* I don't have children
*
* #ORM\Entity */
Class BaseModule extends ModuleSuperClass {}
/**
* I have children
*
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="discr", type="string")
* #ORM\DiscriminatorMap({"page" = "Page"})
*/
Class AdminModule extends ModuleSuperClass {}
/**
* I'm just a child
*
* #ORM\Entity
*/
Class PageModule extends AdminModule {}
your mileage may of course vary, i.e. I would rather have a BaseModule class without children, and then a BaseModule in an entirely different namespace to extend both AdminModule and PageModule from.

How to auto update properties of a doctrine entity?

I've created a doctrine entity class in my symfony 2 project and now I want to auto set a property before saving by the entity manager. Are there some hooks to implement or how can I solve my issue?
Yes, you can:
http://symfony.com/doc/master/book/doctrine.html#lifecycle-callbacks
<?php
namespace MyNS\Dummy;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class Dummy
{
protected $property;
/**
* #ORM\PrePersist
* #ORM\PreUpdate
*/
public function automaticProperty()
{
$this->property = 'whatever';
}
}

Resources