No identifier/primary key specified for Entity why using KNP Translatable - symfony

I am using Knp/DoctrineBehaviors to translate my database content.
I followed the manual and created 2 entities, 1 for non-translatable content and the other for translatable fields.
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Contract\Entity\TranslatableInterface;
use Knp\DoctrineBehaviors\Model\Translatable\TranslatableTrait;
/**
* Class Test
* #package App\Entity
* #ORM\Entity()
*/
class Test implements TranslatableInterface
{
use TranslatableTrait;
/**
* #var integer
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Id()
*/
private $id;
/**
* #return int
*/
public function getId(): int
{
return $this->id;
}
/**
* #param int $id
*/
public function setId(int $id): void
{
$this->id = $id;
}
}
and the Translation entity:
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Contract\Entity\TranslationInterface;
use Knp\DoctrineBehaviors\Model\Translatable\TranslationTrait;
/**
* Class TestTranslation
* #package App\Entity
* #ORM\Entity()
*/
class TestTranslation implements TranslationInterface
{
use TranslationTrait;
/**
* #var
* #ORM\Column(type="string")
*/
private $name;
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param mixed $name
*/
public function setName($name): void
{
$this->name = $name;
}
}
I also added the Bundle in my bundles.php file.
But when I run the command php bin/console doctrine:schema:update --force to create the table in give the error: No identifier/primary key specified for Entity "App\Entity\TestTranslation". Every Entity must have an identifier/p rimary key.

A primary key is missing in your entity "TestTranslation".
In doctrine every entity class must have an identifier/primary key. You can select the field that serves as the identifier with the #Id() annotation or in your case : #ORM\Id().
For example, add this to your entity :
/**
* #var integer
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Id()
*/
private $id;
And then, you can play the command: php bin/console d:s:u --force

In my case, Symfony 3.4, I forgot to register the bundle and set the config.yml as:
knp_doctrine_behaviors:
translatable: true

Related

How to expose a property of related object in serializer configuration in Symfony 2?

I use FOSRestBundle with JMSSerializerBundle in my web service.
Here is a function in my action controller that returns a list of roles. I don't know how to return a "parent_id" field in this list.
<?php
use FOS\RestBundle\Controller\FOSRestController;
class RoleController extends FOSRestController
{
...
/**
* List all roles.
*
* #Annotations\View()
*
* #return array
*/
public function getRolesAction()
{
$roles = $this->repository->findRoles();
$view = $this->view($roles, 200);
return $this->handleView($view);
}
...
}
Here is my role entity. It has properties: id, name and parent. Parent is a role.
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Role
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="AppBundle\Entity\RoleRepository")
*/
class Role
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Role")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id")
*/
private $parent;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
// + getters and setters
...
}
Here is my serializer config file:
AppBundle\Entity\Role:
exclusion_policy: ALL
properties:
id:
expose: true
type: integer
xml_attribute: true
name:
expose: true
type: string
Now getRolesAction in action controller will output JSON with id and name properties. How to add parent_id property to the serializer output?
You could use something like:
/**
* #Serializer\VirtualProperty
* #Serializer\Type("integer")
* #Serializer\SerializedName("parent_id")
*
* #return integer
*/
public function getParentId()
{
return $this->parent->getId();
}
I can't tell you how to transfer the annotation in your config (probably YAML) format, but you'll be able to do that.
IMPORTANT
This only works for serialization - so don't expect this attribute to be deserialized later!!
Alternative:
If you only want to achieve the result you were asking for you could add this to your attribute:
/**
* #Accessor(getter="getParentId",setter="setParent") */
* #Serializer\Type("integer")
* #Serializer\SerializedName("parent_id")
*/
private $parent;
and additionally another getter:
/**
*
* #return integer
*/
public function getParentId()
{
return $this->parent->getId();
}
With the accessor you tell JMS from which getter to get the result to serialize while still being able to use the setter for deserialization.

FOSUserBundle add properties

i've extended FOSUserBundle with my custom User Entity in this way:
<?php
namespace Hu\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Entity\User as BaseUser;
use Symfony\Component\Validator\Constraints as Assert;
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity
*/
class User extends BaseUser
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="fname", type="string", length=255)
* #Assert\NotBlank()
*/
private $fname;
// other fields...
/**
* Set fname
*
* #param string $fname
* #return UserEntity
*/
public function setFname($fname)
{
$this->fname = $fname;
return $this;
}
/**
* Get fname
*
* #return string
*/
public function getFname()
{
return $this->fname;
}
public function __construct()
{
parent::__construct();
// ...
$this->fname = setFname($fname);
}
}
But when i try to load the /register route or to add a new user by:
php app/console fos:user:create
Symfony returns me:
FatalErrorException: Error: Call to undefined function Hu\UserBundle\Entity\setFname() in /Library/WebServer/Documents/sfprojects/quattro/src/Hu/UserBundle/Entity/User.php
What's wrong in the constructor? What i miss?
Thanks a lot,
setFname isn't function, but it is method in your class.
so to execute it you need use $this->setFname($fname).
Also in constructor you shouldn't run methods like setters or getters (if they don't do anything except set variable or get variable)
Your constructor should look like:
public function __construct()
{
parent::__construct();
$this->fname = $fname;
}
the commande " php app/console fos:user:create " uses the setters of the entity User to affect data inputs from the terminal ( in the same way that Forms uses them to affect the data to an object from the < input ... > tag ) .
You can add setters for all the fields you added to your User class to solve this problem
exemple :
/**
* #var string
*
* #ORM\Column(name="fname", type="string", length=255)
* #Assert\NotBlank()
*/
private $fname;
public function setFname($fname) {
$this->fname = $fname;
}

Best practice - Check if Entity exist before presist

Whats the best practice to check if entity fields exist before persisting it.
Here's the example
Entity
class Pile{
/**
* #var \ABC\CoreBundle\Entity\Record
*
* #ORM\OneToMany(targetEntity="Record")
*
*/
private $records;
/**
* #var \CSC\CoreBundle\Entity\Project
*
* #ORM\ManyToOne(targetEntity="Project")
*
*/
private $project;
/**
* #var string
*
* #ORM\Column(name="Block", type="string", length=255)
*/
private $block;
/**
* #var string
*
* #ORM\Column(name="Type", type="string", length=255)
*/
private $type;
}
class Record{
/**
* #var \CSC\CoreBundle\Entity\Pile
*
* #ORM\ManyToOne(targetEntity="Pile")
*
*/
private $records;
}
There are two controllers that handle the CRUD of Pile and Records.
To create Pile there must not be any duplicate fields [project, block, type]
In Record Controllers I could create Pile together with Record.
Here's the problem where and when do I check the db if a similar Pile entity is created?
Whats the Best Practice?
Copy and paste the query checker in both controller?
Can I use $form->valid() to perform any check in PileType class?
Must I use a service and have both controller to call the service?
In entity life-cycle use pre-insert?
Thanks
Therefore, the fields must be unique?
If so, then it is very simple: UniqueEntity
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
...
/**
* #ORM\Entity
* #UniqueEntity(
* fields={"project", "block", "type"}
* )
*/
class Pile{
/**
* #var \ABC\CoreBundle\Entity\Record
*
* #ORM\OneToMany(targetEntity="Record")
*
*/
private $records;
/**
* #var \CSC\CoreBundle\Entity\Project
*
* #ORM\ManyToOne(targetEntity="Project")
*
*/
private $project;
/**
* #var string
*
* #ORM\Column(name="Block", type="string", length=255, unique=true)
*/
private $block;
/**
* #var string
*
* #ORM\Column(name="Type", type="string", length=255, unique=true)
*/
private $type;
}
You can use a custom validation constraint in your form, so that $form->isValid() will do the check.
Follow this documentation entry on How to create a Custom Validation Constraint to create the custom validator and then inject doctrine into it to do the check.
UPDATE: Well, I didn't know there was an UniqueEntity Constraint already included in Symfony.
To inject doctrine do the following:
services:
validator.unique.unique_pile:
class: ABC\CoreBundle\Validator\Constraints\UniquePileValidator
arguments: [#doctrine.orm.entity_manager]
tags:
- { name: validator.constraint_validator, alias: unique_pile }
The validator class might then look like this:
// src/ABC/CoreBundle/Validator/Constraints/UniquePileValidator.php
namespace ABC\CoreBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class UniquePileValidator extends ConstraintValidator
{
protected $em;
function __construct($em) {
$this->em = $em;
}
public function validate($value, Constraint $constraint)
{
$repo = $this->em->getRepository('ABC\CoreBundle\Entity\Record');
$duplicate_project = $repo->findByProject($value);
$duplicate_block = $repo->findByBlock($value);
$duplicate_type = $repo->findByType($value);
if ($duplicate_project || $duplicate_block || $duplicate_type) {
$this->context->addViolation(
$constraint->message,
array('%string%' => $value)
);
}
}
}
And to be complete, the constraint class:
// src/ABC/CoreBundle/Validator/Constraints/ContainsAlphanumeric.php
namespace ABC\CoreBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* #Annotation
*/
class ContainsAlphanumeric extends Constraint
{
public $message = 'This Pile already exists!';
public function validatedBy()
{
return 'unique_pile';
}
}
Should be nearly copy/pasteable...

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.

Doctrine2 - OneToMany/ManyToOne Bidirectional not working

Hi everyone and thanks for help,
I'm currently dealing with a problem while I want to make an OneToMany/ManyToOne Bidirectional Relationship with Doctrine2 (& Symfony2).
Here are my two classes : User (which extends FOSUser) and collection which represents a collection of videos.
Of course, a user can have several collections, but a collection is only related to one user.
/* COLLECTION */
namespace Com\ComBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Com\ComBundle\Entity\Collection
*
* #ORM\Table(name="collection")
* #ORM\HasLifecycleCallbacks
*/
class Collection
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Com\UserBundle\Entity\User", inversedBy="collections")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* Set user
*
* #param Com\UserBundle\Entity\User $user
* #return Collection
*/
public function setUser(\Com\UserBundle\Entity\User $user) {
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return Com\UserBundle\Entity\User User
*/
public function getUser() {
return $this->user;
}
}
And user,
/* USER */
namespace Com\UserBundle\Entity;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\Table(name="user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/*
* #ORM\OneToMany(targetEntity="Com\ComBundle\Entity\Collection", mappedBy="user")
*/
private $collections;
public function __construct()
{
parent::__construct();
$this->collections = \Doctrine\Common\Collections\ArrayCollection();
}
}
When I use the doctrine:generate:entities command, it does not generate the methods relative to $collections (get/add/remove), and even if I wrote it myself, it does not work.
getCollections() return NULL for example.
What am I missing in this code ?

Resources