Association mapping with mapped superclass - php

In my vendor bundle, I have 2 mapped superclass : BaseSite and BaseSection (which are abstract).
In my application bundle, I have 2 entities that extends the 2 mapped superclass.
Everything works so far: I have access to the fields defined in the superclasses and I can add new ones in my application bundle if needed.
The problem is when I am trying to define my association mapping between those entities. (manyToOne between BaseSection and BaseSite).
If I define it in the BaseSection mapped superclass, I am able to run the command app/console doctrine:generate:entities AcmeDemoBundle, but it doesn't work when
I try to create the tables: (app/console doctrine:schema:update --dump-sql)
CREATE TABLE Section (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, siteId INT DEFAULT NULL, INDEX IDX_95E06DEFFADB670C (siteId), PRIMARY KEY(id)) ENGINE = InnoDB;
CREATE TABLE Site (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id)) ENGINE = InnoDB;
ALTER TABLE Section ADD CONSTRAINT FK_95E06DEFFADB670C FOREIGN KEY (siteId) REFERENCES BaseSite(id) ON DELETE CASCADE
As you can see, it tries to reference the foreign key on a table that doesn't exists (BaseSite instead of Site). I'm guessing this is because the mapped superclass isn't aware of the table name defined in the application entity.
I could define the association mapping on the application entities instead, but that would mean that if someone wanted to use my bundle, he would have to define the mapping himself, which I would like to avoid.
Is there another way to do this or maybe I'm just missing something?
Here is my code:
Vendor :
File: vendor\bundles\Acme\DemoBundle\Resources\config\doctrine\BaseSite.orm.yml
Acme\DemoBundle\Entity\BaseSite:
type: mappedSuperclass
fields:
id:
type: integer
id: true
generator:
strategy: AUTO
name:
type: string
length: 255
nullable: false
// ...
File: vendor\bundles\Acme\DemoBundle\Resources\config\doctrine\BaseSection.orm.yml
Acme\DemoBundle\Entity\BaseSection:
type: mappedSuperclass
fields:
id:
type: integer
id: true
generator:
strategy: AUTO
name:
type: string
length: 255
nullable: false
// ...
manyToOne:
site:
targetEntity: Acme\DemoBundle\Entity\BaseSite
joinColumn:
name: siteId
referencedColumnName: id
onDelete: cascade
Application:
File: src\Application\Acme\DemoBundle\Resources\config\doctrine\Site.orm.yml
Application\Acme\DemoBundle\Entity\Site:
type: entity
table: Site
File: src\Application\Acme\DemoBundle\Entity\Site.php
<?php
namespace Application\Acme\DemoBundle\Entity;
use Acme\DemoBundle\Entity\BaseSite;
class Site extends BaseSite
{
}
File: src\Application\Acme\DemoBundle\Resources\config\doctrine\Section.orm.yml
Application\Acme\DemoBundle\Entity\Section:
type: entity
table: Section
File: src\Application\Acme\DemoBundle\Entity\Section.php
<?php
namespace Application\Acme\DemoBundle\Entity;
use Acme\DemoBundle\Entity\BaseSection;
class Section extends BaseSection
{
}

After reading the Doctrine manual on Inheritance Mapping its says:
This means that One-To-Many associations are not possible on a mapped superclass at all
It might be worth looking at the table inheritance features.

Related

Invalid row key specified, data-load fixtures error Symfony 1.4

I have a problem with a symfony 1.4 setup :
I created the schema.yml:
BlogCategory:
actAs: { Timestampable: ~ }
columns:
name: { type: string(255), notnull: true, unique: true }
BlogPost:
actAs: { Timestampable: ~ }
columns:
category_id: { type: integer, notnull: true }
title: { type: string(255), notnull: true }
body: { type: string(255), notnull: true }
relations:
BlogCategory: { onDelete: CASCADE, local: category_id, foreign: id, foreignAlias: BlogPosts }
(based on jobeet tutorial )
then it generated me the table schema.sql :
CREATE TABLE blog_category (id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(255) NOT NULL UNIQUE, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL);
CREATE TABLE blog_post (id INTEGER PRIMARY KEY AUTOINCREMENT, category_id INTEGER NOT NULL, title VARCHAR(255) NOT NULL, body VARCHAR(255) NOT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL);
which seems right to me.
And to finish I tried to add fixtures and load the data, my fixtures are as follows:
data/fixtures/categories.yml:
BlogCategory:
design:
name: Design
programming:
name: Programming
management:
name: Management
administrator :
name: Administrator
data/fixtures/posts.yml
BlogPost:
initial_post_1:
BlogCategory : design
title: Initial post 1
body: This post is an initial test number 1
initial_post_2:
BlogCategory : design
title: Initial post 2
body: This post is an initial test number 2
The php symfony doctrine:data-load command is generating me the categories but not the posts, so I tried :
php symfony doctrine:data-load data/fixtures/posts.yml
Invalid row key specified: (blog_category) design, referred to in
(blog_post) initial_post_1
Any idea why I can't laod the posts ? I've already tried to delete the db re-generate etc...
Okey so after many tests and git reset --hard HEAD to fix my datas, it seems that I had a problem with my BlogPost.class.php which contained an empty save() method override that was messing things up.
Plus, to rewrite data in the right way the command to use is:
php symfony doctrine:build --all --and-load
and not :
php symfony doctrine:data-load
Hope it could help anyone with the same problem !

Symfony2/Doctrine can't find mapping file for custom Repository class

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)

symfony2 doctrine mapping bidirectional links from existing DB

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!

Cascaded persist not working (Doctrine ORM + Symfony 2)

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);

How to declare Doctrines Inheritance in a fixtures.yml file?

I have a problem with the inheritance of Doctrine within my fixtures.yml file of Symfony.
Imagine the following:
NewsAuthor:
inheritance:
extends: sfGuardUser
type: simple
Now I want to declare the default guard user User_admin of the fixtures file of the sfDoctrineGuardPlugin to be the author of the first dummy article of my News model. But how to do this?
I tried the following:
NewsAuthor:
AuthorAdmin:
sfGuardUser: User_admin
But then I get this error of the doctrine:data-load task:
Unknown record property / related component "sfguarduser" on "NewsAuthor"
I use Symfony 1.4.8 with Doctrine 1.2.3.
Thank you in anticipation,
Lemmi
You're misunderstanding how Doctrine's inheritance works. Under simple inheritance, NewsAuthor wouldn't have a reference to sfGuardUser, it would have all of the columns and relations that sfGuardUser has plus the ones you define. Inheritance is a bad approach here.
Instead, you want a NewsAuthor to have a reference to sfGuardUser. Your schema in this case would look like:
NewsAuthor:
columns:
user_id:
type: integer(4)
notnull: true
unique: true #if one-to-one correspondence
relations:
User:
class: sfGuardUser
local: user_id
foreignType: one
Your fixtures would then look as they did before:
NewsAuthor:
AuthorAdmin:
sfGuardUser: User_admin
You could then access the sfGuardUser object on a NewsAuthor object via $newsAuthor->User, similarly, you can access NewsAuthor on an sfGuardUser instance via $user->NewsAuthor.

Categories