doctrine 2 - persisting an entity with composite key - php

A Doctrine 2 Entity with a composite key:
/**
* #Entity
*/
class Test
{
/**
* #Id
* #Column (type="integer", length=11, name="id")
*
*/
protected $id = null;
/**
* #Id
* #Column (type="integer", length=11, name="idtwo")
*
*/
protected $idtwo = null;
public function setIdTwo($id)
{
$this->idtwo = $id;
}
public function setId($id)
{
$this->id = $id;
}
}
Saving the Entity
$test = new Test();
$test->setId(1);
$test->setIdTwo(1);
$em->persist($test);
DB Table:
CREATE TABLE `Bella_Test` (
`id` int(11) NOT NULL,
`idtwo` int(11) NOT NULL,
PRIMARY KEY (`id`,`idtwo`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Expected result: a row is added to the db table with two id fields, both with a value of 1.
Actual result: No row is added to the db table. No exception is thrown.
Question: What is going on?

You can use a try catch block to see what happens
try{
$em->flush(); //don't forget flush after persisting an object
}
catch(Exception $e){
echo 'Flush Operation Failed: '.$e->getMessage();
}
Other assumption, in my opinion, your entity table name and DB table name may not match each other. I think it's not a bad idea to give a try
/**
* #Entity
* #Table(name="Bella_Test")
*/
.
.
.

Related

Symfony Doctrine two keys for different purposes, one AUTO_INCREMENT

I'm new to Symfony and Doctrine and using the latest versions, am trying to create a Model with two key fields (attributes) for different purposes. $id is not the Primary Key but is AUTO_INCREMENT in MySQL/MariaDB. $key is string length=100 mapped to CHAR[100] and is the primary key and #ORM\Id. Even though this is what I'm trying to do, I don't think it's a supported configuration. I want to be able to merge() using key but also want to have the AI column for references (joining) because in part as I think it will have better performance, and even if not I want it anyway :) The thing is the data source which I use to populate may send multiple objects with the same key in which case I want them to persist to the same record, the last object the last to update the db, no duplicate keys.
I read somewhere, to use the DB implementation of AI and on non primary key, I may have to write a custom Hydrator. I have little idea what files to create and where to put them, and what mapping I have to do, or how exactly to do it. Hope someone can help.
<?php
// src/Entity/View.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\ViewRepository")
*/
class View
{
/**
* #ORM\Column(type="integer", name="created_by")
*/
private $created_by;
/**
* #ORM\Column(type="integer", name="updated_by")
*/
private $updated_by;
/**
* #ORM\Column(type="bigint", name="id")
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #ORM\Id
* #ORM\Column(type="string", length=100, name="`key`")
*/
private $key;
/**
* #ORM\Column(type="json", name="`value`")
*/
private $value;
public function getCreatedBy()
{
return $this->created_by;
}
public function setCreatedBy($value)
{
$this->created_by = $value;
}
public function getUpdatedBy()
{
return $this->updated_by;
}
public function setUpdatedBy($value)
{
$this->updated_by = $value;
}
public function getId()
{
return $this->id;
}
public function setId($value)
{
$this->id = $value;
}
public function getKey()
{
return $this->key;
}
public function setKey($value)
{
$this->key = $value;
}
public function getValue()
{
return $this->value;
}
public function setValue($value)
{
$this->value = $value;
}
}
/* View */
CREATE TABLE IF NOT EXISTS `View` (
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by INT UNSIGNED NOT NULL,
updated_by INT UNSIGNED NOT NULL,
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`key` CHAR(100) NOT NULL,
`value` JSON NOT NULL,
PRIMARY KEY PK_VIEW (`key`),
UNIQUE KEY UQ_VIEW (id)
) CHARSET = latin1;
$entity = new View();
$entity->setCreatedBy(0);
$entity->setUpdatedBy(0);
$entity->setKey($messageId);
$entity->setValue($json);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->merge($entity);
$entityManager->flush();

How to map a foreign key on an entity both as field and mapping in Doctrine?

Background (short version): Symfony4 application in which I use a custom data layer over doctrine entity layer so the business logic layer is not dependent on the database schema. This is not changeable for the moment.
In some cases it's easier to use have the foreign key mapped as a field:
/**
* #var string
*
* #ORM\Column(name="article_id", type="string")
*/
protected $articleId;
but in other cases for the same entity I need the relation:
/**
* #var Article
*
* #ORM\OneToOne(targetEntity="Article")
* #ORM\JoinColumn(name="article_id", referencedColumnName="id")
*/
protected $article;
to not have the same entity in 2 copies, I added the snippets above in one single class. If I hydrate the relation, everything works well ( $articleId is ignored ) but if only the $articleId is provided and $articles is null, doctrine will insert in DB null for the foreign key.
I found a possible solution: I parse the class metadata, and if in $class->associationMappings['joinColumnFieldNames'] exists fields that I also have as fields in the object, I remove the association mapping:
doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php
private function doMerge($entity, array &$visited, $prevManagedCopy = null, array $assoc = [])
{
$oid = spl_object_hash($entity);
if (isset($visited[$oid])) {
$managedCopy = $visited[$oid];
if ($prevManagedCopy !== null) {
$this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy);
}
return $managedCopy;
}
$class = $this->em->getClassMetadata(get_class($entity));
/* Code that I added */
foreach ($class->associationMappings as $key => $mapping) {
$joinColumns = $mapping['joinColumnFieldNames'] ?? [];
if (array_intersect($joinColumns, $class->getColumnNames())) {
unset($class->associationMappings[$key]);
unset($class->reflFields[$key]);
}
}
/* .... */
}
Is it ethically to do such thing?

Error with flush, try catch and object

It is throwing an error
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'complete' cannot be null
How do I "tell" the Entity Manager not to consider the fruit object since it is throwing an error?
I have an object like:
class Fruit {
/**
* #Id #Column(type="integer")
* #GeneratedValue(strategy="AUTO")
*/
private $id;
/** #Column(type="string", length=10, nullable=true) */
private $name;
/** #Column(type="boolean") */
private $complete;
/* Omitted to make it simple */
}
And a method like:
public function uploadAction(){
try{
/* Omitted to make it simple */
while ($data = fgetcsv($fic, 1024, $delimiter)) {
try {
$fruit = $fruitManager->createFruit($data);
$outcome_fruit = $fruit->id;
} catch (\Exception $e) {
$outcome_details[] = $e->getMessage();
}
}
fclose($fic);
#unlink($file_path);
$csv_data = $this->view->io->build_csv_file($outcome_details);
$csv_import->output_csv = base64_encode(gzencode($csv_data));
$this->em->persist($csv_import);
// error here
$this->em->flush();
//
} catch (\Exception $e) {
print $e->getMessage();
exit;
}
}
My fruitManager
class FruitManager{
public function createFruit($name){
$fruit = new \Entities\Fruit;
$fruit->name = $name;
throw new Exception("Exception.");
$fruit->complete = 1;
$this->em->persist($fruit);
$this->em->flush();
}
}
You have few options:
update your entity allowing null values using ORM to allow null values for this field ie /** #Column(type="boolean", nullable=true) */
update your entity with default value using ORM ie /** #ORM\Column(type="boolean", nullable=false, options={"default" : false}) */
assign default value in the entity constructor
assign default value using private $complete = false; like Freelancer said
I'd prefer one of the first 2 options depending on logic needs.
Thanks.
try by setting private $complete = false; or true as you want.

An exception occurred while executing 'UPDATE... ': Integrity constraint violation: 1062 Duplicate entry '' for key 'PRIMARY'

DefaultFormHandler.php
protected function onSuccess($object)
{
$artistmember= new ArtistMember();
$artistmember->setMemberId($object->getManager()->getId());
$artistmember->setArtistId($object->getId());
$this->em->merge($artistmember);
$this->em->flush();
return $object;
}
ArtistMember
/**
* #ORM\Entity
* #ORM\Table(name="artist_member")
*/
class ArtistMember
{
/**
* #ORM\Id
* #ORM\Column(name="artist_id", type="integer")
*/
private $artist_id;
/**
* #ORM\Column(name="member_id", type="integer")
*/
private $member_id;
/**
* #param mixed $artist_id
*/
public function setArtistId($artist_id)
{
$this->artist_id = $artist_id;
}
/**
* #param mixed $member_id
*/
public function setMemberId($member_id)
{
$this->member_id = $member_id;
}
}
entity artist get manager()
/**
* Set manager
*
* #param Member $manager
*/
public function setManager($manager)
{
$this->manager = $manager;
if ( ! $this->members->contains( $manager) ) {
$this->members->add( $manager );
}
}
/**
* Get manager
*
* #return \MainBundle\Entity\Member
*/
public function getManager()
{
return $this->manager;
}
I have 2 tables: Artist table and member table
In artist table I have the id and manager_id and in member table I have member_id and artist_id.
Update query works fine if the member table contains only one artist_id and member_id.
Eg: artist-id=1,manager_id =10.
But I get the above error if I try to update any of the manager id in this case:
eg: artist_id=1
member_id=10
artist_id=1
member_id =20
If I try to update the member id 20 to 30 or 10 to 30 I am getting the error.
The issue is resolved with this solution: But still is there a better way to do it?Also is it possible to check whether the artist_id and member_id are already existing in artist_member table.
The error is self explanatory:
An exception occurred while executing 'UPDATE… ': Integrity constraint violation: 1062 Duplicate entry '' for key 'PRIMARY'
The issue is when you try to update the manager id 20 to 30 or 10 to 30, the new value you are trying to update are already present in the column.

Could not load other mapping of entity in doctrine2 and zend 1

Anyone, please help for me this issue. I'm the newbie of Doctrine. After some time to configure the doctrine(version 2.3) working with zend(version 1.10.8). All working fine except the last step. I have a table "test table" like this
CREATE TABLE IF NOT EXISTS `testtable` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(250) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=13 ;
INSERT INTO `testtable` (`id`, `name`) VALUES
(1, 'test1'),
(2, 'what');
This is the Entity annotations
<?php
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="testtable")
* #ORM\Entity
*
*/
class Tallcat_Doctrine_Entity_Test
{
/**
* #ORM\Column(type="string")
*/
private $name;
/**
* #ORM\Id #ORM\Column
* #ORM\GeneratedValue
*/
private $id;
/**
* #param string $name
*/
public function setName($name)
{
$this->name = (string)$name;
}
/**
* #return string
*/
public function getName()
{
return $this->name;
}
}
And in the controller of zend, i call this for testing:
class DoctrineController extends Zend_Controller_Action
{
protected $em = null;
public function init()
{
$this->em = \Zend_Registry::get('doctrine')->getEntityManager();
}
public function indexAction()
{
try{
$test = new Tallcat_Doctrine_Entity_Test();
$testMap = $this->em->getRepository('Tallcat_Doctrine_Entity_Test')- >findAll();
echo '<pre>';
print_r($testMap);
echo '</pre>';
die();
}catch(Exception $ex) {
print_r($ex);die();
}
}
}
And this is the result:
Array
(
[0] => Tallcat_Doctrine_Entity_Test Object
(
[name:Tallcat_Doctrine_Entity_Test:private] =>
[id:Tallcat_Doctrine_Entity_Test:private] => 1
)
[1] => Tallcat_Doctrine_Entity_Test Object
(
[name:Tallcat_Doctrine_Entity_Test:private] =>
[id:Tallcat_Doctrine_Entity_Test:private] => 2
)
)
I don't know what wrong with the file name, it cannot load. I tried to save one record, it can save the Id, except the file Name.
Someone could help please.
I solved it myself. The problem is:
The Doctrine cached the mapping before load, so the old code didn't update. So i restart the computer(stupid way) to clear cache. And it works perfect.
Thanks

Categories