I started working with symfony several months ago and there is one thing keeps bothering me all the time. It is when I have a one-to-many relationship in Doctrine and I try to insert something into the database. Here's an example:
Broker.orm.yml
Acme\DemoBundle\Entity\Broker:
type: entity
table: brokers
repositoryClass: BrokerRepository
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
name:
type: string
length: 255
slug:
type: string
length: 64
oneToMany:
accountTypes:
targetEntity: Acme\DemoBundle\Entity\AccountType
mappedBy: broker
cascade: ["persist"]
AccountType.orm.yml
Acme\DemoBundle\Entity\AccountType:
type: entity
table: account_types
repositoryClass: AccountTypeRepository
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
name:
type: string
length: 255
slug:
type: string
length: 64
manyToOne:
broker:
targetEntity: Acme\DemoBundle\Entity\Broker
inversedBy: accountTypes
joinColumn:
name: broker_id
referencedColumn: id
Then is try to save it to the database like this.
$accountType = new AccountType();
$accountType->setName("blabla");
// additional data to accountType
$broker->addAccountType($accountType);
$em->persist($broker);
$em->flush();
The strange thing is that it works prefrectly with only one tiny problem. Broker is updated, and AccountType is inserted into the database but the accountType doesn't have any relations with the Broker. In other words, when I check in the database the broker_id fields reamains untouched and contains NULL.
If I add $accountType->setBroker($broker) manually, it works. But I started to use Sonata Admin Bundle where there is much more complicated to do this and I don't really need a complex admin system. So I just want a fast development on it and without this "feature" it's nearly impossible.
And anyways, if I add something to a collection of an Object, it should know which object is its parent, right? :)
Thanks for your help in advance!
class Broker
{
public function addAccountType($accountType)
{
$this->accountTypes[] = $accountType;
// *** This is what you are missing ***
$accountType->setBroker($this);
Related
I love doctrine but I have some problems with mapping/annotations. At start I used mapping files. Then I converted in into annotations. Now I want o create custom repository class so I did as I read here: http://symfony.com/doc/current/book/doctrine.html#custom-repository-classes.
Unfortunately now I have an error:
No mapping file found named '\src\Vendor\ProductBundle\Resources\config\doctrine/SynchronizationSettingRepository.orm.yml' for class 'Vendor\ProductBundle\Entity\SynchronizationSettingRepository'.
Of course I don't have this file becouse I don't use mapping anymore.
I've added:
* #ORM\Entity(repositoryClass="Vendor\ProductBundle\Entity\SynchronizationSettingRepository")
to parent and regenerated entities. I have regenerate Entites by command php app/console doctrine:generate:entities VendorProductBundle and still nothing. Regural and doctrine meadata cache is clear.
Here is a YML which from I want to generate custom Repository one more time:
Vendor\ProductBundle\Entity\SynchronizationSetting:
type: entity
table: synchronization_setting
repositoryClass: Vendor\SynchronizationSetting\Entity\SynchronizationSettingRepository
indexes:
id_product:
columns:
- id_product
id:
id:
type: integer
nullable: false
unsigned: true
comment: ''
id: true
generator:
strategy: IDENTITY
fields:
open:
type: string
nullable: true
length: 1
fixed: true
comment: ''
default: '0'
internet:
type: string
nullable: true
length: 1
fixed: true
comment: ''
default: '0'
manyToOne:
idProduct:
targetEntity: Product
cascade: { }
mappedBy: null
inversedBy: null
joinColumns:
id_product:
referencedColumnName: id
orphanRemoval: false
lifecycleCallbacks: { }
And here is a repository class:
<?php
// src/Acme/StoreBundle/Entity/ProductRepository.php
namespace Vendor\ProductBundle\Entity;
use Doctrine\ORM\EntityRepository;
class SynchronizationSettingRepository extends EntityRepository
{
public function findAllOrderedByName()
{
return $this->getEntityManager()
->createQuery(
'SELECT p FROM AcmeStoreBundle:Product p ORDER BY p.name ASC'
)
->getResult();
}
}
I think that adding #ORM\Entity did very little good as complete .php class file gets overwritten as soon as you run the doctrine:generate:entities. You need to add repositotyClass to your YML file instead.
If you switched to annotations for good, those .yml files (actually whole doctrine directory within config) are useless, apart as being intermediate files for generating annotaion-based entities.
Another thing: It seams that Doctrine thinks you have a entity with name ending with "Repository".
Can you show us the content of YML file? If you don't have it (as you said), generating entities will not be possible. You can always generate annotaion-based entities directly (no need for YML intermediates)
Is it possible using Yaml orm files to specify a base Entity that all of the Entities will share, I can compare this to C# ADO.NET where you can specify a class that holds all of the data that will be common among all of your tables
For example, say I have the below YAML mapping for Doctrine:
src\SITEBUNDLE\Entity\User:
REALTYBLOG\Entity\User:
type: entity
table: users
id:
id:
type:integer
generator: { strategy: AUTO }
fields:
firstName:
type: string
length: 255
lastName:
type: string
length: 255
middleInital:
type: string
length: 1
username:
type:
length: 26
passwordHash:
type: string
length: 255
createdOn:
type: dateTime
createdBy:
type: string
length: 26
modifiedOn:
type: dateTime
modifiedBy:
type: string
deletedOn:
type: dateTime
Now lets say that I have created multiple other YAML configs and all of them share the createdOn, createdBy, modifiedOn, modifiedBy, deletedOn columns. Can I specify an overlaying orm file that I can include in all of my orm files so I don't have to redundantly add them to every orm file?
Yes you can. It's quite simple.
TextItem:
columns:
topic: string(255)
Comment:
inheritance:
extends: TextItem
type: concrete
columns:
content: string(300)
More information: http://docs.doctrine-project.org/projects/doctrine1/en/latest/en/manual/yaml-schema-files.html -> inheritance / simple inheritance
Hi everyone and thanks for help in advance!
I'm a newbe in a Symfony2 framework and I faced a question:
How to create bidirectional relationships from existing database?
At first I created database for my project and than I mapped it to the yml files;
Simply, DB looks like this:
Table user:
CREATE TABLE user (
`id` INT NOT NULL AUTO_INCREMENT ,
`login` VARCHAR(255) NULL ,
`password` VARCHAR(255) NULL ,
`customer_id` INT NOT NULL ,
PRIMARY KEY (`id`) ,
INDEX `fk_user_customer1_idx` (`customer_id` ASC) ,
CONSTRAINT `fk_user_customer1`
FOREIGN KEY (`customer_id` )
REFERENCES `customer` (`id` )
) ENGINE = InnoDB
Table customer:
CREATE TABLE IF NOT EXISTS `customer` (
`id` INT NOT NULL AUTO_INCREMENT ,
`surname` VARCHAR(45) NULL ,
`name` VARCHAR(45) NULL ,
`midname` VARCHAR(45) NULL ,
PRIMARY KEY (`id`)
) ENGINE = InnoDB
If I'm right, "user" have Many-to-One relationship to the "customer"; and "user" is an owning side, "customer" is an inverse side;
Then I run these commands:
php app/console doctrine:mapping:import ShadowTestBundle yml --force
And got the result:
Shadow\TestBundle\Entity\User:
type: entity
table: user
fields:
id:
id: true
type: integer
unsigned: false
nullable: false
generator:
strategy: IDENTITY
login:
type: string
length: 255
fixed: false
nullable: true
password:
type: string
length: 255
fixed: false
nullable: true
manyToOne:
customer:
targetEntity: Customer
cascade: { }
mappedBy: null
inversedBy: null
joinColumns:
customer_id:
referencedColumnName: id
orphanRemoval: false
lifecycleCallbacks: { }
Shadow\TestBundle\Entity\Customer:
type: entity
table: customer
fields:
id:
id: true
type: integer
unsigned: false
nullable: false
generator:
strategy: IDENTITY
surname:
type: string
length: 45
fixed: false
nullable: true
name:
type: string
length: 45
fixed: false
nullable: true
midname:
type: string
length: 45
fixed: false
nullable: true
lifecycleCallbacks: { }
And conforming entities by run command:
php app/console doctrine:generate:entities ShadowTestBundle
Entities reflects correctly the yml-files;
But both yml-files and entities use only unidirectional links; is it possible to generate bi-directional links, or I must write it manually?
As far as I see, it should look like that:
Shadow\TestBundle\Entity\Customer:
type: entity
table: customer
fields:
...
midname:
type: string
length: 45
fixed: false
nullable: true
oneToMany:
user:
targetEntity: User
mappedBy: cart
lifecycleCallbacks: { }
And I also have a little subquestion:
Why on the owning side (User), which generated by Doctrine, field "inversedBy" is null?
The imported mappings generated by app/console doctrine:mapping:import are not always correctly reflecting the complete database structure i.e. when it comes to non-primary keys.
The mapping's inversedBy attribute is set to null because doctrine can't guess your owning-side-entity's desired $property name for storing the inverse-side entity from a database - therefore the mapping is generated without inversedBy set...
... which results in the auto-generated/expected property-name being the camelCase representation of the targetEntity as default / convention.
If you are not importing hundreds of tables i would recommend targeting these little corrections by hand and not linger over the import command.
it's worth checking the documentation.
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/tools.html#reverse-engineering
From the document,
Reverse Engineering is a one-time process that can get you started with a project. Converting an existing database schema into mapping files only detects about 70-80% of the necessary mapping information. Additionally the detection from an existing database cannot detect inverse associations, inheritance types, entities with foreign keys as primary keys and many of the semantical operations on associations such as cascade.
Hope this helps. Cheers!
Given this setup for my Doctrine 2 Entities:
App\Bundle\LorumBundle\Entity\Node:
type: entity
table: node
fields:
id:
id: true
type: integer
unsigned: false
nullable: false
generator:
strategy: IDENTITY
created:
type: datetime
inheritanceType: SINGLE_TABLE
discriminatorColumn:
name: type
type: string
length: 255
discriminatorMap:
a: a
b: b
c: c
App\Bundle\LorumBundle\Entity\A:
type: entity
fields:
status:
type: boolean
App\Bundle\LorumBundle\Entity\B:
type: entity
fields:
status:
type: boolean
App\Bundle\LorumBundle\Entity\C:
type: entity
fields:
title:
type: string
Now what I want to get is basically a mixed list of Entities of the type A & B (not C) with the status == true.
I could write a Query like this - using the instance ofoperator to limit the result to the subclasses i want of course but i will get an Error because the property I want to match against (status) is not mapped in the Superclass even tough all the Entities i want to match against have it:
$queryBuilder->select('Node');
$queryBuilder->from('App\Bundle\LorumBundle\Entity\Node','Node');
$queryBuilder->add('where',$queryBuilder->expr()->orx(
'Offer INSTANCE OF AppLorumBundle:A',
'Offer INSTANCE OF AppLorumBundle:B'
));
$queryBuilder->where($queryBuilder->expr()->eq('Node.status', '?1'));
$queryBuilder->setParameter(1, true);
$queryBuilder->orderBy('Node.created', 'asc');
$queryBuilder->setFirstResult( 0 );
$queryBuilder->setMaxResults( 200 );
Is there any way to do this, short of writing your own persister and hack it into Doctrine2?
Unfortunately its not an Option for me to just add the Information to the Superclass (in my real scenario this situation mostly applies to relations which i don't want to be loaded eagerly with every subclass)
You can use UNION and work with queries for entities A and B
build query for entity A
build query for entity B
build query for limit and order using UNION for queries A and B
no need to do any hacks for Doctrine library
I have a entity called Container in a Symfony application, which I have included in the schema.yml file:
Container:
columns:
id: { type: integer, primary: true, autoincrement: true }
name: { type: string(127), notnull: true }
Strain:
columns:
id: { type: integer, primary: true, autoincrement: true }
...
container_id: { type: integer }
...
relations:
Container: { foreignAlias: Strains }
Then I have regenerated the models, forms and filters using the symfony doctrine:build --all-classes task.
Now when I try to use $strain->getContainer(), e.g. in a showSuccess action, it returns no object at all. I have double-checked that container_id has a reference to a record in container table.
Moreover, when I try to edit a Strain object and unlink the relationship with Container, the form is saved correctly, but the container_id column keeps the old value.
Do you know if Container is a reserved word or something like that in Symfony or Doctrine? What can be happening?
Thanks!
No, it isn't a reserved word.
(In fact, I have a model in my project with a Container relation).
Have you tried setting the local property on Container relation to container_id?