Doctrine HasLifecycleCallbacks PrePersist|PreUpdate don't fire - doctrine-orm

I'm using Doctrine 2 with ZF2 and the Doctrine-Module.
I've written an Entity that needs a PreUpdate|PrePersist, because Doctrine doesn't allow
Date|Datetime in a primary key:
<?php
namespace Application\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
*
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
* #ORM\Table(name="sample")
*/
class Sample
{
/**
*
* #ORM\Id
* #ORM\Column(type="string")
* #var integer
*/
protected $runmode;
/**
*
* #ORM\Id
* #ORM\Column(type="date")
* #var DateTime
*/
protected $date;
public function getRunmode()
{
return $this->runmode;
}
public function setRunmode($runmode)
{
$this->runmode = $runmode;
return $this;
}
public function getDate()
{
return $this->date;
}
public function setDate($date)
{
$this->date = $date;
return $this;
}
/**
*
* #ORM\PreUpdate
* #ORM\PrePersist
*/
protected function formatDate()
{
die('preUpdate, prePersist');
if ($this->date instanceof \DateTime) {
$this->date = $this->date->format('Y-m-d');
}
return $this;
}
}
The Problem is now, if i set a DateTime as a Date I get the message:
"Object of class DateTime could not be converted to string"
because it doesn't walk into the formatDate.

First of all, since you mapped the field Sample#date as datetime, it should always be either null or an instance of DateTime.
Therefore, you should typehint your setDate method as following:
public function setDate(\DateTime $date = null)
{
$this->date = $date;
return $this;
}
Also, your lifecycle callback is not invoked because the visibility of method formatDate is protected, therefore not accessible by the ORM. Change it into public and it will work. No conversion should be necessary anyway, so you can get rid of it.

Related

Overriding discriminator mapping on join table

The project works with multiple user logins (plumber, builder), the relevant information for each entity is saved in their respective tables. The structure for them is supplied below.
We've been storing the information for suppliers and have now gathered a few hundred entries. Suppliers are now requiring a login to access the system.
The current flow with the discriminator map is it creates an entry in the Users table and then saves in the respective user type table with the id being that of the user.id.
User.id = 5 => plumber.id = 5
User.id = 6 => builder.id = 6
User.id = 7 => plumber.id = 7
Suppliers have had their own table with their own incrementing ids which would cause clashes with the DiscriminatorMap(). Is there a way to have suppliers be unique and connect on a supplier.user_id instead of supplier.id, like the other tables?
<?php
namespace Project\Entities;
abstract class BaseEntity
{
public static $idCol = 'id';
public static $joins = [];
public static $orderBy = [];
}
?>
<?php
namespace Project\Entities;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping AS ORM;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
/**
* #ORM\Entity(repositoryClass="Project\Repositories\User\UserRepository")
* #ORM\Table(name="user")
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="type", type="string")
* #ORM\DiscriminatorMap({"user"="User", "plumber"="Plumber", "builder"="Builder"})
* #ORM\HasLifecycleCallbacks()
*/
class User extends BaseEntity implements Authenticatable
{
use \LaravelDoctrine\ORM\Auth\Authenticatable;
use \Project\Entities\Traits\HasContactDetails;
const TYPE_USER = "user";
const TYPE_PLUMBER = "plumber";
const TYPE_BUILDER = "builder";
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #var int
*/
protected $id;
/**
* #ORM\Column(type="string", unique=true, nullable=false)
* #var string
*/
protected $login;
/**
* #ORM\Column(type="text")
* #var string
*/
protected $password;
/**
* #return int
*/
public function getId() {
return $this->id;
}
/**
* #return string
*/
public function getLogin() {
return $this->login;
}
/**
* #param string $login
*/
public function setLogin($login) {
$this->login = $login;
}
public function getAuthPassword() {
return $this->password;
}
/**
* Encrypt password when inserting
* #ORM\PrePersist
*/
public function onPrePersist() {
$this->encryptPassword();
}
/**
* Encrypt password when updating
* #ORM\PreUpdate
*/
public function onPreUpdate(\Doctrine\ORM\Event\PreUpdateEventArgs $event) {
if ($event->hasChangedField('password')) {
$this->encryptPassword();
}
}
/**
*
* #return string
*/
public function getType() {
if ($this instanceof \Project\Entities\Plumber) {
return self::TYPE_PLUMBER;
}
if ($this instanceof \Project\Entities\Builder) {
return self::TYPE_BUILDER;
}
return self::TYPE_USER;
}
public function getAuthIdentifierName() {
return "login";
}
.....
}
?>
User roles extending
<?php
namespace Project\Entities;
use Doctrine\ORM\Mapping AS ORM;
/**
* #ORM\Entity(repositoryClass="Project\Repositories\Plumber\PlumberRepository")
* #ORM\Table(name="plumber")
*/
class Plumber extends User
{
.....
}
?>
<?php
namespace Project\Entities;
use Doctrine\ORM\Mapping AS ORM;
/**
* #ORM\Entity(repositoryClass="Project\Repositories\Builder\BuilderRepository")
* #ORM\Table(name="builder")
*/
class Builder extends User
{
.....
}
?>
The current supplier entity.
<?php
namespace Project\Entities;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping AS ORM;
/**
* #ORM\Entity(repositoryClass="Project\Repositories\Supplier\SupplierRepository")
* #ORM\Table(name="supplier")
*/
class Supplier extends BaseEntity
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #var int
*/
protected $id;
/**
* #ORM\Column(type="string")
* #var string
*/
protected $name;
/**
* #ORM\ManyToOne(targetEntity="Region")
* #var Region
*/
protected $region;
public function getId() {
return $this->id;
}
public function getName() {
return $this->name;
}
public function getRegion() {
return $this->region;
}
public function setId($id) {
$this->id = $id;
}
public function setName($name) {
$this->name = $name;
}
/**
* #param Region $region
*/
public function setRegion($region) {
$this->region = $region;
}
.....
}
?>

Doctrine association persist

I have a problem with doctrine. I have two entities.
FriendRequest
class FriendRequest
{
/** #ORM\Id #ORM\Column(type="integer") #ORM\GeneratedValue **/
protected $id;
/**
* First person from friendship
*
* #ORM\OneToOne(targetEntity="User")
* #ORM\JoinColumn(name="id", referencedColumnName="id")
*/
protected $from;
/**
* Second person from friendship
*
* #ORM\OneToOne(targetEntity="User")
* #ORM\JoinColumn(name="id", referencedColumnName="id")
*/
protected $to;
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #return mixed
*/
public function getFrom()
{
return $this->from;
}
/**
* #param mixed $from
*/
public function setFrom($from)
{
$this->from = $from;
}
/**
* #return mixed
*/
public function getTo()
{
return $this->to;
}
/**
* #param mixed $to
*/
public function setTo($to)
{
$this->to = $to;
}
}
And User
/**
* #ORM\Entity #ORM\Table(name="users")
*/
class User
{
/** #ORM\Id #ORM\Column(type="integer") #ORM\GeneratedValue **/
protected $id;
/** #ORM\Column(type="string") **/
protected $email;
/** #ORM\Column(type="string") **/
protected $password;
/** #ORM\Column(type="string") **/
protected $name;
/** #ORM\Column(type="string") **/
protected $surname;
/** #ORM\Column(type="date") **/
protected $date;
/** #ORM\Column(type="string") **/
protected $sex;
/**
* #return mixed
*/
public function getSex()
{
return $this->sex;
}
/**
* #param mixed $sex
*/
public function setSex($sex)
{
$this->sex = $sex;
}
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #param mixed $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* #return mixed
*/
public function getEmail()
{
return $this->email;
}
/**
* #param mixed $email
*/
public function setEmail($email)
{
$this->email = $email;
}
/**
* #return mixed
*/
public function getPassword()
{
return $this->password;
}
/**
* #param mixed $password
*/
public function setPassword($password)
{
$this->password = $password;
}
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param mixed $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* #return mixed
*/
public function getSurname()
{
return $this->surname;
}
/**
* #param mixed $surname
*/
public function setSurname($surname)
{
$this->surname = $surname;
}
/**
* #return mixed
*/
public function getDate()
{
return $this->date;
}
/**
* #param mixed $date
*/
public function setDate($date)
{
$this->date = $date;
}
}
I tried to create the new friendship request.
$from = $this->entity_manager->getRepository(User::class)->findOneBy(
["id" => $my_id]
);
$to = $this->entity_manager->getRepository(User::class)->findOneBy(
["id" => $target_user_id]
);
/** #var FriendRequest $friend_request */
$friend_request = new FriendRequest();
$friend_request->setFrom($from);
$friend_request->setTo($to);
$this->entity_manager->persist($friend_request);
$this->entity_manager->flush();
But it does not work. Doctrine shows me error:
An exception occurred while executing 'INSERT INTO friendRequest (id) VALUES (?)' with params [186]:
SQLSTATE[HY000]: General error: 1364 Field 'from' doesn't have a default value.
I tried to print $from ad $to, but they are correct. Is there someone who know what does it mean? I spent a lot of time with it, but I dont know...
I think your issue comes from the declaration of the $from and $to attributes in the FriendRequest entity class, if you try to update your database model you are going to get an error like this:
[Doctrine\ORM\Mapping\MappingException]
Duplicate definition of column 'id' on entity 'FriendRequest' in a field or discriminator column mapping.
Since you have assigned the same name 'id' for two different attributes. Probably you have in your database two columns From and To, but they are not mapping with your model, that's why you get an error saying from doesn't have a default value. Since the column name in the database are not sync with the name of the attributes in your entity.
Assuming you already have a FriendRequest table and you are using 'from' and 'to' as the column names this should be the update of the FriendRequest entity class.
class FriendRequest
{
/** #ORM\Id #ORM\Column(type="integer") #ORM\GeneratedValue **/
protected $id;
/**
* First person from friendship
*
* #ORM\OneToOne(targetEntity="User")
* #ORM\JoinColumn(name="from", referencedColumnName="id")
*/
protected $from;
/**
* Second person from friendship
*
* #ORM\OneToOne(targetEntity="User")
* #ORM\JoinColumn(name="to", referencedColumnName="id")
*/
protected $to;
...
}

zf2 doctrine 2 - Output OnetoOne Unidirectional

After annotating an OneToOne Unidirectional, i want to output the joined column, without using a form.
One People Entity has got a column to store the id of Country Entity.
What i can do: I can store the id of the country into the People Entity using a form with a dropdown select, which is bound to the Country Entity.
Problem: I can not enter the value country of the, hopefully correct, joined table.
People Entity:
<?php
namespace People\Entity;
use Doctrine\ORM\Mapping as ORM;
// ...
/**
* A people entity.
*
* #ORM\Entity
* #ORM\Table(name="icd_people")
* #property int $id
// ...
* #property string $ic_hq_country
*/
class People implements InputFilterAwareInterface
{
protected $inputFilter;
/**
* #ORM\Id
* #ORM\Column(type="integer");
*/
protected $id;
/**
* #ORM\Column(type="integer")
* #ORM\OneToOne(targetEntity="Country")
* #ORM\JoinColumn(name="ic_hq_country", referencedColumnName="id")
*/
protected $ic_hq_country;
// getter and setter
}
The Country Entity:
<?php
namespace People\Entity;
use Doctrine\ORM\Mapping as ORM;
//...
/**
* A Country entity.
*
* #ORM\Entity
* #ORM\Table(name="pre_country")
* #property int $id
* #property string $country
*/
class Country implements InputFilterAwareInterface
{
protected $inputFilter;
/**
* #ORM\Id
* #ORM\Column(type="integer");
*/
protected $id;
/**
* #ORM\Column(type="string")
*/
protected $country;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set country
*
* #param string $country
* #return Country
*/
public function setCountry($country)
{
$this->country = $country;
return $this;
}
/**
* Get country
*
* #return string
*/
public function getCountry()
{
return $this->country;
}
/**
* Convert the object to an array.
*
* #return array
*/
public function getArrayCopy()
{
return get_object_vars($this);
}
public function setInputFilter(InputFilterInterface $inputFilter)
{
throw new \Exception("Not used");
}
public function getInputFilter()
{
throw new \Exception("Not used");
}
}
The Controller Action:
public function indexAction()
{
$userid = $this->zfcUserAuthentication()->getIdentity()->getId();
return new ViewModel(array(
'pea' => $this->getEntityManager()->find('People\Entity\People', $userid),
));
}
The View giving the id of the country, but not the name:
<?php echo $this->escapeHtml($pea->ic_hq_country);?>
I actually expected something like this being possible, to output the country name and not the id:
<?php echo $this->escapeHtml($pea->country);?>
Thank you for reading, and for any help, which could lead me into the right direction!
You should not use the #Column anotation in the $ic_hq_country field of the Peopleentity.
/**
* #ORM\OneToOne(targetEntity="Country")
* #ORM\JoinColumn(name="ic_hq_country", referencedColumnName="id")
*/
protected $ic_hq_country;
like this, hopefully ic_hq_country will be a proxy to the entity instead of the id.
so in your view you can use:
<?php echo $pea->ic_hq_country->getId();?>
and also
<?php echo $this->escapeHtml($pea->ic_hq_country->getCountry());?>

Doctrine self referencing entity relationship throws error

I have a self referencing entity in Doctrine but I keep getting the following error when I try to persist them:
PHP Catchable fatal error: Object of class Category could not be converted to string in vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php on line 165
Example:
$category1 = new Category();
$category1->setName("Foo");
$category1->setParent( NULL );
$category2 = new Category();
$category2->setName("Bar");
$category2->setParent( $category1 );
$manager->persist( $category1 );
$manager->persist( $category2 );
$manager->flush();
My entity looks like this:
/**
* #ORM\Table(name="categories")
* #ORM\Entity
*/
class Category {
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #ORM\Column(type="string", length=64, unique=true)
*/
protected $name;
/**
* #ORM\Column(nullable=true)
* #ORM\ManyToOne(targetEntity="Category", inversedBy="children")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id")
*/
protected $parent;
/**
* #ORM\OneToMany(targetEntity="Category", mappedBy="parent")
*/
protected $children;
public function __construct()
{
$this->children = new ArrayCollection();
}
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function setName( $name )
{
$this->name = $name;
return $this;
}
public function getParent()
{
return $this->parent;
}
public function setParent( Category $parent = NULL )
{
$this->parent = $parent;
return $this;
}
public function getChildren()
{
return $this->children;
}
public function setChildren( ArrayCollection $children )
{
$this->children = $children;
return $this;
}
}
I have Googled and compared my code to other examples but I can't seem to spot the problem. I'm obviously overlooking something, but what?
Try to use
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="children")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=true)
*/
protected $parent;
But I also recommend you to set onDelete="SET NULL" on the parent column, like this:
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="children")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="SET NULL")
*/
public $parent;
I know it's not part of the question, but you are probably going to need this in the near future. If you want the children column to give you the categories in a order that the user can choose, you may want to add a integer column named order and define children like this:
/**
* #ORM\OneToMany(targetEntity="Category", mappedBy="parent")
* #ORM\OrderBy({"order" = "ASC"})
*/
public $children;

How remove registry from Many To Many relationship - Doctrine 2

I'm trying remove a registry from my database using doctrine 2, but I'm getting the follow error:
Catchable fatal error: Argument 1 passed to Doctrine\ORM\Mapping\DefaultQuoteStrategy::getJoinTableName() must be an array, null given, called in /home/triangulum/www/pedal/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php on line 1020 and defined in /home/triangulum/www/pedal/vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/DefaultQuoteStrategy.php on line 86
I have 3 tables:
grupo = id, name
permissao = id, name
grupo_permissao = grupo_id, permissao_id
My entitys:
Grupo:
<?php
namespace User\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="grupo")
* #ORM\Entity(repositoryClass="User\Entity\GrupoRepository")
*/
class Grupo {
public function __construct($options=null) {
Configurator::configure($this, $options);
$this->permissoes = new ArrayCollection();
}
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
* #var int
*/
protected $id;
/**
* #ORM\Column(type="text")
* #var string
*/
protected $nome;
/**
* #ORM\ManyToMany(targetEntity="User\Entity\Permissao", mappedBy="grupo", cascade={"remove"})
*/
protected $permissoes;
public function getPermissoes()
{
return $this->permissoes;
}
public function getId() {
return $this->id;
}
public function setId($id) {
$this->id = $id;
}
public function getNome() {
return $this->nome;
}
public function setNome($nome) {
$this->nome = $nome;
}
public function toArray()
{
return array
(
'id' => $this->getId(),
'nome' => $this->getNome()
);
}
}
?>
Permissao:
<?php
namespace User\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="permissao")
* #ORM\Entity(repositoryClass="User\Entity\PermissaoRepository")
*/
class Permissao {
public function __construct($options=null) {
Configurator::configure($this, $options);
$this->grupos = new ArrayCollection();
}
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
* #var int
*/
protected $id;
/**
* #ORM\Column(type="text")
* #var string
*/
protected $nome;
/**
* #ORM\ManyToMany(targetEntity="User\Entity\Grupo", inversedBy="permissao", cascade={"persist", "remove"})
* #ORM\JoinTable(name="grupo_permissao",
* joinColumns={#ORM\JoinColumn(name="permissao_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="grupo_id", referencedColumnName="id")}
* )
*/
protected $grupos;
public function getGrupos() {
return $this->grupos;
}
public function getId() {
return $this->id;
}
public function setId($id) {
$this->id = $id;
}
public function getNome() {
return $this->nome;
}
public function setNome($nome) {
$this->nome = $nome;
}
public function toArray()
{
return array
(
'id' => $this->getId(),
'nome' => $this->getNome()
);
}
}
?>
remove:
public function delete($id)
{
$entity = $this->entityManager->find('User\Entity\Grupo', $id);
if($entity)
{
$this->entityManager->remove($entity);
$this->entityManager->flush();
return $id;
}
}
What am I doing wrong?
I think you have a problem with your mappedBy and inversedBy attributes in the manyToMany annotations. They should match the other entities property name, grupos and permissoes. Try this:
/**
* #ORM\ManyToMany(targetEntity="User\Entity\Permissao", mappedBy="grupos", cascade={"remove"})
*/
protected $permissoes;
/**
* #ORM\ManyToMany(targetEntity="User\Entity\Grupo", inversedBy="permissoes", cascade={"persist", "remove"})
* #ORM\JoinTable(name="grupo_permissao",
* joinColumns={#ORM\JoinColumn(name="permissao_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="grupo_id", referencedColumnName="id")}
* )
*/
protected $grupos;