Doctrine2 prevent default relation index - symfony

I have defined a new entity in symfony 2 using doctrine annotations. It has a many-to-one relation to another table and an unique index, using the same column (parent_id, subset_index):
/**
* #ORM\Table(name="mapping", uniqueConstraints={#ORM\UniqueConstraint(name="parent_subset_index", columns={"parent_id", "subset_index"})})
* #ORM\Entity
*/
class Mapping
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Record")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=false, onDelete="RESTRICT")
*/
private $parent;
/**
* #ORM\Column(name="subset_index", type="integer")
*/
private $subsetIndex;
}
A doctrine:migrations:diff generates the follwing MySQL statement:
CREATE TABLE mapping (
id INT AUTO_INCREMENT NOT NULL,
parent_id INT NOT NULL,
subset_index INT NOT NULL,
INDEX IDX_D87F7E0C727ACA70 (parent_id),
UNIQUE INDEX parent_subset_index (parent_id, subset_index),
PRIMARY KEY(id)
)
As you see I end up with two indices over parent_id, the IDX_D87F7E0C727ACA70 index is unnecessary.
How can I prevent doctrine from generating this index? I know I can edit the migration file, but then the index would be recreated everytime I run another doctrine:migrations:diff.

Related

How to modify Symfony ORM insert\update query

I have project that is migrate to Symfony, that project have multiple tables,and also some tables are migrated to ORM, but now i need to incert/update from Symfony to table that have Entity but not managed by ORM. Problem consist in not null columns that require some value and in Entity I cannot define that value because of table relations.
It posible to edit MySql query before they submited to Database.
For example i have Entity:
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* User
*
* #ORM\Table(name="p_user")
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
*/
class User
{
/**
* #var int
*
* #ORM\Column(name="user_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string|null
*
* #ORM\Column(name="name", type="string", length=55, nullable=true)
*/
private $name;
/**
* #var Permission
*
* #ORM\ManyToOne(targetEntity="Permission", inversedBy="user", cascade={"persist", "remove"})
* #ORM\JoinColumn(name="permission_id", referencedColumnName="permission_id", onDelete="CASCADE")
*/
private $permission;
}
permission_id can be null but in database is not null with default value 0, same for name but with default value ''.
That mean when I make flush, ORM execute INSERT INTO p_user (name, permission_id) VALUES ('name', null), but I want also to execute INSERT INTO p_user (name) VALUES ('name').
It's possible to do that I wanted.
To achieve this you can provide default values.
private $permission = 0;
private $name = '';

symfony doctrine 2 OneToMany - association to the inverse side field error

I want to connect two entities
Dish that is in some DishCategory
Dish (category_id) with DishCategory (id)
There is an error:
The association AppBundle\Entity\Dish#categoryId refers to the inverse side field AppBundle\Entity\DishCategory#category_id which does not exist.
These are my entity classes
Dish Entity
class Dish
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*#ORM\ManyToOne(targetEntity = "DishCategory",inversedBy="category_id",cascade={"persist"})
* #ORM\JoinColumn(name="id",referencedColumnName="id")
*/
private $categoryId;
}
DishCategory Entity
class DishCategory
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\OneToMany(targetEntity="Dish", mappedBy="category_id")
* #ORM\JoinColumn(name="category_id",referencedColumnName="id")
*/
private $id;
}
In DishController I run this function to repository
$dishes = $em->getRepository('AppBundle:Dish')->findAllAsArray();
And that how DishRepository looks
public function findAllAsArray()
{
return $q = $this->createQueryBuilder('d')
->join('d.categoryId','c')
->select('d.id as id_dish','c.id as id_cat','d.price')
->orderBy('c.position', 'asc')
->orderBy('d.position', 'asc')
->getQuery()
->getResult(Query::HYDRATE_ARRAY);
}
I have read many tutorials about OneToMany but still I cant find where is the problem :(
Still getting error:
The association AppBundle\Entity\Dish#categoryId refers to the inverse
side field AppBundle\Entity\DishCategory#category_id which does not
exist.
:(
I guess category_id is a foreign key. Don't map foreign keys to fields in an entit because:
Foreign keys have no meaning whatsoever in an object model. Foreign keys are how a relational database establishes relationships. Your object model establishes relationships through object references. Thus mapping foreign keys to object fields heavily leaks details of the relational model into the object model, something you really should not do.
your DishCategory entity is the Owning side because it holds the foreign key. By the way update your code as below:
Dish entity
class Dish
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="DishCategory", inversedBy="dishes")
* #ORM\JoinColumn(name="dish_category_id",referencedColumnName="id")
*/
private $dishCategory;
}
DishCategory entity
class DishCategory
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="Dish", mappedBy="dishCategory")
*/
private $dishes;
public function __construct()
{
$this->dishes = new ArrayCollection();
}
}
Then in your DishRepository
public function findAllAsArray()
{
return $q = $this->createQueryBuilder('d')
->join('d.category','c')
->select('d.id as id_dish','c.id as id_cat','d.price')
->addOrderBy('c.position', 'asc')
->addOrderBy('d.position', 'asc')
->getQuery()
->getResult(Query::HYDRATE_ARRAY)
;
}
But I don't see the point of doing a double orderBy because multiple calls to orderBy do not stack, use addOrderBy if you want to achieve this. And also Query::HYDRATE_ARRAY is not necessary, I guess, because you will have an array due to the custom query, not and entity.

Issue with ManyToMany relationship

I have 2 enteties.
A package, and a StockItem.
The Package can have many StockItems, and the StockItem can belong to Many packages. A ManyToMany seems most appropriate.
The issue appears when i attempt to add 2 of the same stockitems to a package, we get an Integraty violation:
{"code":500,"message":"An exception occurred while executing 'INSERT INTO StockItem_In_Package (package_id, stockitem_id) VALUES (?, ?)' with params [4, 1]:\n\nSQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '4-1' for key 'PRIMARY'"}
Since Package(id 4), creates 2 relationships with stockItem 1.
4-1 and 4-1
Is it possible to get around this in any way?
Is it possible to add a third column named id to the ManyToMany table, or add one named ItemCountInPackage, and just increment that by one when the same is added to a package? What would be the best solution.
Package Entity, only inserting relevant code:
/**
* Package
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="IHEnterprise\LogisticsBundle\Entity\PackageRepository")
* #ORM\HasLifecycleCallbacks
*
* #ExclusionPolicy("all")
*
*/
class Package
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #Expose
*/
private $id;
/**
* #ORM\ManyToMany(targetEntity="IHEnterprise\LogisticsBundle\Entity\StockItem", cascade={"all"}, indexBy="package_id")
* #ORM\JoinTable(name="StockItem_In_Package",
* joinColumns={#ORM\JoinColumn(name="package_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="stockitem_id", referencedColumnName="id")}
* )
* #Expose
* #Assert\NotNull()
**/
private $stockItems;
}
StockItem Entity, only inserting relevant code:
/**
* StockItem
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="IHEnterprise\LogisticsBundle\Entity\StockItemRepository")
* #ORM\HasLifecycleCallbacks
*
* #ExclusionPolicy("all")
*
*/
class StockItem
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #Expose
*/
private $id;
}
I do not need to keep track of what StockItems belong to which packages, only what packages contain which stockitems.
You can't add a third column to your ManyToMany relationship. So you can't have the same StockItem twice in one package.
To have more than one piece of the same StockItem in one package, you have to create a third entity with an unique id, and a ManyToOne relationship to StockItem and Package (and a OneToMany relationship in StockItem & Package to your third entity).
It looks like you are trying to insert an item that was already in the database as a new record. To prevent such an error do the following :
/**
* #desc Add only new items to a single package
* #param \AcmeBundle\Entity\Package $package
* #param ArrayCollection<StockItem> $stockItems
*/
public function addStockItemsAction(Package $package, $stockItems) {
foreach ($stockItems as $stockItem) {
if ($package->contains($stockItem) === false) {
$package->addStockItem($stockItem);
}
}
}

Entity containing other entities without having a table

I have an Entity ( Invoice ) which is purely for calculation purposes and has no table, that associates with two other entities having relations by tables. (Although there are so many other entities involved ).
class Row{
/**
* #var integer
*
* #ORM\Column(name="row_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="File")
* #ORM\JoinColumn(name="file_id", referencedColumnName="file_id")
*/
protected $file;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="date")
*/
private $date;
}
class File
{
/**
* #var integer
*
* #ORM\Column(name="file_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
}
class Invoice
{
/**
* #ORM\Id
* #ORM\Column(name="invoice_id", type="integer")
* #ORM\GeneratedValue
*/
protected $id = null;
/**
* #ORM\OneToMany(targetEntity="Row", mappedBy="row_id")
*/
protected $row;
/**
* #ORM\OneToMany(targetEntity="File", mappedBy="file_id")
*/
protected $file;
}
I want to be able to query for Invoices :
$sDate = //Some date
$this->getEntityManager()
->createQuery("SELECT Invoice, Row, File
FROM
ReportsEntitiesBundle:Invoice Invoice
LEFT JOIN
Row.row Row
LEFT JOIN
Row.file File
WHERE date=:date"
)
->setParaMeter(':date', $sDate)
->setFirstResult($iPage*$iLimit)
->setMaxResults($iLimit)
->getResult();
The questions :
# Doctrine tries to query the database, how can I prevent that and have it find the relevant entities?
# How can I relate the date ( which is in Row entity and cannot be in Invoice ) to the query?
Later this Invoice will become a part of another big entity for calculating/search purposes.
Thank you
Short Answer: You can't
Long Answer : You can't because an entity with #ORM annotations means its persisted to a database - querying that entity relates to querying a database table. Why not just create the table ?!?!?
You need somewhere to persist the association between file and row - a database table is a perfect place !!!!
Update
Just to clarify ... an Entity is just a standard class - it has properties and methods ... just like any other class - When you issue doctrine based commands it uses the annotations within the entities to configure the tables / columns / relationships etc if remove those you can use it however you like ... but you will need to populate the values to use it and you wont be able to use it in a Doctrine query and it obviously wont be persisted !
You can use a read-only entity. It's contents are backed by a view which you create manually in SQL.
PHP:
/** #ORM\Entity(readOnly =true) */
class InvoiceView
{ ...
SQL:
CREATE VIEW invoice_view AS (
SELECT ...

Symfony2 Doctrine2 column being set to null

I have a system that took form information detailing a project, added it to a project table and is meant to add an entry into an assigned projects table to associate user with project (point of this is allowing multiple users for each project). Anyway I got this working without foreign keys, struggled to add them but eventually got them.
Unfortunately this additional has caused this error 'SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'projectId' cannot be null' whenever something is added to the assignedProjects table.
So my question is, have I missed something in my codes?
The code to add a new row to assignedProjects:
$assignedProject = new AssignedProjects();
$assignedProject->setProjectId($project->getId());
$assignedProject->setUserId($user[0]['id']);
$em = $this->getDoctrine()->getEntityManager();
$em->persist($assignedProject);
$em->flush();
The code for the assignProjects entity:
class AssignedProjects
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer $projectId
*
* #ORM\Column(name="projectId", type="integer")
*/
private $projectId;
/**
* #ORM\ManyToOne(targetEntity="Projects", inversedBy="assignment")
* #ORM\JoinColumn(name="projectId", referencedColumnName="id")
*/
private $project;
/**
* #var integer $UserId
*
* #ORM\Column(name="userId", type="integer")
*/
private $userId;
/**
* #ORM\ManyToOne(targetEntity="Dev\UserBundle\Entity\User", inversedBy="assignment")
* #ORM\JoinColumn(name="userId", referencedColumnName="id")
*/
private $user;
(followed by the usual getters and setters)
and the project tables entity is:
class Projects
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $projectName
*
* #ORM\Column(name="projectName", type="string", length=255)
*/
private $projectName;
/**
* #ORM\OneToMany(targetEntity="AssignedProjects", mappedBy="project")
*/
protected $assignment;
Any help would be much appreciated!
Either you use the ProjectId and UserId columns and manage the relationship manually (not recommended) or you use the doctrine relationships(recommended), but don´t do both things. If you go for the second option, don´t include the projectId and userId columns, they are automatically created for you by doctrine. So, your AssignedProjects class should be:
class AssignedProjects {
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id * #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Projects", inversedBy="assignment")
* #ORM\JoinColumn(name="projectId", referencedColumnName="id")
*/
private $project;
/**
* #ORM\ManyToOne(targetEntity="Dev\UserBundle\Entity\User", inversedBy="assignment")
* #ORM\JoinColumn(name="userId", referencedColumnName="id")
*/
private $user;
and in your controller you would do:
$assignedProject = new AssignedProjects();
$assignedProject->setProject($project);
$assignedProject->setUser($user[0]);
$em = $this->getDoctrine()->getEntityManager();
$em->persist($assignedProject);
$em->flush();
Note that I am setting the Project and User fields, not the ids
By the way, unless you need to save extra data about this project assignement (things like the date or similar), you can declare a direct ManyToMany relationship between User and Project and do away with this class, Doctrine would generate the needed table by itself
With Doctrine2, you don't have to declare the foreign key (projectId) but only the association (project). So you can delete $projectId property, as well as setProjectId ans getProjectId methods. Same fix for $user...
Instead, you will use setProject like that :
$assignedProject = new AssignedProjects();
$assignedProject->setProject($project);
$assignedProject->setUser($user[0]);
$em = $this->getDoctrine()->getEntityManager();
$em->persist($assignedProject);
$em->flush();
Have a look to Doctrine2 documentation, it will help you, for sure !
http://docs.doctrine-project.org/projects/doctrine-orm/en/2.1/reference/association-mapping.html

Resources