Problem with api-plateform when using 2 level of inheritance with doctrine entity - symfony

I have a problem in a 2 level of inheritance structure with api-platfom but maybe with doctrine too
I have two question about this code :
doctrine is well generating a discr column in the MyParent class, but not in MyChild class for some reason... I don't think it is normal as how to know that an instance is MyChildChild if there is no discr column in MyChild ?
with api-platform, I have some errors as soon as I have 2 levels of inheritance. As soon as I remove one of the "extends MyChild" or "extends MyParent" api-platform behaviour is ok but loose the fields of the removed class (normal)
Is there something I'm doing wrong ? I have already use multiple level of inheritance with doctrine and I don't remember that I had this behaviour (only one discr column for 2 classes). And for api-platform, I'm just discovering so maybe I missed something
Thank you in advance
RV
Here is my code
// src/Entity/MyParent.php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Repository\MyParentRepository;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\MyChild;
/**
* #ApiResource()
* #ORM\Entity(repositoryClass=MyParentRepository::class)
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="discr", type="string")
* #ORM\DiscriminatorMap({"child"="MyChild"})
*/
class MyParent
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $parentAttribute;
public function getId(): ?int
{
return $this->id;
}
public function getParentAttribute(): ?string
{
return $this->parentAttribute;
}
public function setParentAttribute(string $parentAttribute): self
{
$this->parentAttribute = $parentAttribute;
return $this;
}
}
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Repository\MyChildRepository;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\MyChildChild;
use App\Entity\MyParent;
/**
* #ApiResource()
* #ORM\Entity(repositoryClass=MyChildRepository::class)
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="discr", type="string")
* #ORM\DiscriminatorMap({"childchild"="MyChildChild"})
*/
class MyChild extends MyParent
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $childAttribute;
public function getId(): ?int
{
return $this->id;
}
public function getChildAttribute(): ?string
{
return $this->childAttribute;
}
public function setChildAttribute(string $childAttribute): self
{
$this->childAttribute = $childAttribute;
return $this;
}
}
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Repository\MyChildChildRepository;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\MyChild;
/**
* #ApiResource()
* #ORM\Entity(repositoryClass=MyChildChildRepository::class)
*/
class MyChildChild extends MyChild
{
/**
* #ORM\Column(type="string", length=255)
*/
private $MyChildChildAttribute;
public function getMyChildChildAttribute(): ?string
{
return $this->MyChildChildAttribute;
}
public function setMyChildChildAttribute(string $MyChildChildAttribute): self
{
$this->MyChildChildAttribute = $MyChildChildAttribute;
return $this;
}
}

Related

symfony - getting another field from a many-to-one to add to dbtable with orm

I have a many-to-one association in my entity and i want to grab another field to put the value in a db table instead of the title field.
my many are categories which can be set to a product. this all works fine.
However i have an additional productcode field where i want to grab the category letter instead of title.
my category entity
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity()
* #ORM\HasLifecycleCallbacks()
*/
class Category
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string")
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="categoryletter", type="string")
*/
private $categoryletter;
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getCategoryletter(): ?string
{
return $this->categoryletter;
}
public function setCategoryletter(string $categoryletter): self
{
$this->categoryletter = $categoryletter;
return $this;
}
/**
* #return string
*/
public function __toString()
{
return $this->title;
}
}
in my product.php entity i have the following code
/**
* #var Category
*
* #ORM\ManyToOne(targetEntity="Category")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $category;
where i have an additional field to set a partical productcode based on category etc. but i don't want the category title but the associated categoryletter
/**
* #ORM\PrePersist
* #ORM\PreUpdate
*/
public function setProductcode()
{
{
$part = $this->category;
$this->productcode = $part;
}
I don't know how to grab another field of the category entity. If i change function __toString to categoryletter instead of title i do get the letter, but i want to stick with the title field just for general purpose.How do i get the categoryletter field? or is this not possible in a direct way?
If the orm is properly made between the two entities you can just use the getter :
/**
* set productCode
*
* #return Product
*/
public function setProductcode()
{
$category = $this->category;
$this->productcode = $category->getCategoryletter();
return $this;
}

dql query error class is not defined

Trying to conver my sql queries into dql, seems im doing something wrong.
I need basic joins, something like this.
SELECT a.id, a.title, u.name FROM articles JOIN users ON a.author_id = u.id
Tried
SELECT a.id, a.title, u.name FROM Article a JOIN User u WITH a.author_id = u.id
Getting error
[Semantical Error] line 0, col 34 near 'Article a JOIN': Error: Class 'Article' is not defined.
How should i define? Could you give me please a right solution?
Edit:
Article Entity
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="articles")
*/
class Article
{
/**
* #var integer $id
*
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string")
*/
protected $title;
/**
* #ORM\Column(type="integer", name="author_id")
*/
protected $authorId;
/**
* #ORM\Column(type="datetime", name="creation_date")
*/
protected $creationDate;
/**
* #ORM\Column(type="string", name="short_content")
*/
protected $shortContent;
/**
* #ORM\Column(type="string")
*/
protected $content;
public function getId()
{
return $this->id;
}
public function getTitle()
{
return $this->title;
}
public function getAuthorId()
{
return $this->authorId;
}
public function getCreationDate()
{
return $this->creationDate;
}
public function getShortContent()
{
return $this->shortContent;
}
public function getContent()
{
return $this->content;
}
}
User Entity
<?php
namespace AppBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="bigint")
*/
protected $phone;
/**
* #ORM\Column(type="string")
*/
protected $gender;
/**
* #ORM\Column(type="string")
*/
protected $about;
public function getPhone()
{
return $this->phone;
}
public function getGender()
{
return $this->gender;
}
public function getAbout()
{
return $this->about;
}
}
Refer to the entities in your DQL by their namespaces, i.e. AppBundle:Article and AppBundle:User, that should make the error go away.
Use association mapping (old docs) instead of authorId in your entity, this way Doctrine will take care of loading the author:
/**
* #ORM\ManyToOne(targetEntity="User")
* #ORM\JoinColumn(name="author_id", referencedColumnName="id")
**/
private $author;
public function getAuthor() {
return $this->author;
}
public function setAuthor($author) {
$this->author = $author;
}
Your query would be reduced to:
SELECT a FROM AppBundle:Article a ORDER BY a.creationDate DESC
Once you have loaded an article, you can conveniently access the author:
...
$author = $article->getAuthor();

No Metadata Classes to process. creating schema

I would like to create my database , using symfony2 +Run command +doctrine:schema:create:...
but I have this message in my output : No Metadata Classes to process
PS: I tried to clear the cache. What should I do? Here is an example for my entity:
<?php
namespace MyApp\EspritBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraint as Assert;
/**
* #ORM\Entity
*/
class Rayon {
/**
* #ORM\GeneratedValue
* #ORM\Id
* #ORM\Column(type="integer")
*/
private $id;
/**
*#ORM\Column(type="string",length=255)
*/
private $libelle;
public function getId() {
return $this->id;
}
public function getLibelle() {
return $this->libelle;
}
public function setId($id) {
$this->id = $id;
}
public function setLibelle($libelle) {
$this->libelle = $libelle;
}
}

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();
}
}

One-To-Many Relation in Symfony 2 with Doctrine

I've looked at literally tons of questions/answers on Stack Overflow and other places on the web, but cannot find a resolution to this problem.
Before I explain the problem, I have:
stripped back my entities so that they only have the minimum attributes and methods
have cleared the doctrine query cache / metadata cache
dropped and recreated the schema
checked my spelling
I have the following two entities:
<?php
namespace Docker\ApiBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Source
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="integer")
* #ORM\ManyToOne(targetEntity="Project",inversedBy="sources")
* #ORM\JoinColumn(referencedColumnName="id")
*/
private $project;
}
<?php
namespace Docker\ApiBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Project
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="Source", mappedBy="project")
*/
private $sources;
public function __construct() {
$this->sources = new ArrayCollection();
}
public function getSources() {
return $this->sources;
}
}
So a many 'sources' can belong to one 'project'.
In my controller I have:
$em = $this->getDoctrine()->getManager();
$project = $em->find('Docker\ApiBundle\Entity\Project', 1);
$sources = $project->getSources()->toArray();
I have tried lots of things but I always get:
Notice: Undefined index: project in /.../www/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php line 1577
Like I say, I know there are a lot of questions going around about this, but none of the accepted answers fix my problem.
This all looks pretty fundamental to using Doctrine2 so not sure what I am doing wrong - it could be something really obvious.
Any help would be appreciated.
You have:
/**
* #ORM\Column(type="integer")
* #ORM\ManyToOne(targetEntity="Project",inversedBy="sources")
* #ORM\JoinColumn(referencedColumnName="id")
*/
private $project;
Remove:
#ORM\Column(type="integer")
from annotation.
If this is exactly your code, i dont see a namespace in your Project class. Try adding the namespace line "namespace Docker\ApiBundle\Entity;".
If this is the same folder no need for "use" but if it s part of other bundle or folder try putting a line like "use Docker\ApiBundle\Entity\Project;"
in your Source class. I Hope it helps..
Otherwise :
<?php
namespace Azimut\ApiBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Source
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
private $name;
/**
* #ORM\Column(type="integer")
* #ORM\ManyToOne(targetEntity="Azimut\ApiBundle\Entity\Project", inversedBy="sources")
* #ORM\JoinColumn(onDelete="CASCADE")
*/
private $project;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set project
*
* #param integer $project
* #return Source
*/
public function setProject($project)
{
$this->project = $project;
return $this;
}
/**
* Get project
*
* #return integer
*/
public function getProject()
{
return $this->project;
}
}
<?php
namespace Azimut\ApiBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
*/
class Project
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="Azimut\ApiBundle\Entity\Source", mappedBy="object", cascade={"all"})
*/
private $sources;
public function __construct() {
$this->sources = new ArrayCollection();
}
public function getSources() {
return $this->sources;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Add sources
*
* #param \Azimut\ApiBundle\Entity\Source $sources
* #return Project
*/
public function addSource(\Azimut\ApiBundle\Entity\Source $sources)
{
$this->sources[] = $sources;
return $this;
}
/**
* Remove sources
*
* #param \Azimut\ApiBundle\Entity\Source $sources
*/
public function removeSource(\Azimut\ApiBundle\Entity\Source $sources)
{
$this->sources->removeElement($sources);
}
}
and little part of the controller:
public function helloAction()
{
$id = 1;
$em = $this->getDoctrine()->getManager();
$project = $em->getRepository('Azimut\ApiBundle\Entity\Project')->find($id);
$source1 = $em->getRepository('Azimut\ApiBundle\Entity\Source')->find(3);
$source2 = $em->getRepository('Azimut\ApiBundle\Entity\Source')->find(5);
$project->addSource($source1);
$sources = array();
$sources = $project->getSources();
var_dump($sources);
return ....
}
and this works just fine for me.

Resources