Without scrutinizing why I want this (it may sound like a bad approach, but I have good reason) I want to know if there is a way in the standard-framework-edition 3.1+ to create a relational association to an entity that may not exist...
Firstly I do realize this determines the schema and that's fine. So if an entity does not exist, it doesn't create a foreign key and the field is always null, or if the target entity does exist, it creates the foreign key and the field works like a normal association...
Secondly, this only changes project to project, and may change down the line as an update to which I realize a manual schema update could be necessary.
Preferably without 3rd party bundle dependencies... hoping for the standard framework to do this,
Anybody?
Thanks in advance
Edit
I am using annotations in my entities with doctrine ORM
Furthermore
The simplest version of why I am doing this is because certain bundles are optional project-to-project, and bundle A may make use of entities in bundle B only if it is present. I have considered using services and if container->has then container->get, or the XML on-invalid="null" approach, but that doesn't address property persistence. I was happy with storing a non-mapped value as a custom relational field, which is fine, just lengthier and wondered if perhaps there was a way Doctrine could ignore a missing targetEntity...
Hm, perhaps I misunderstand your question, but this sounds like a normal 'nullable' association to me?
Create your assocation via annotation:
/**
*
* #var Child
* #ORM\ManyToOne(targetEntity="Child")
*/
private $child;
and use
setChild(Child $child = null)
{
$this->child = $child;
}
as a Setter to allow nullable values.
And your getter might look like:
getChild()
{
return $this->child;
}
In case there isn't any child it will return null.
I will keep the other answer as it responds to the question for a 'nullable association target' live data.
This is the answer for a 'nullable association target' meta data which is a different thing.
OP asks to provide a targetEntity in the metadata which cannot exist in his case, e.g. is not there in a different bundle (or whatever OP's mysterious reason might be).
In that case I recommend to build upon Doctrine's TargetEntityListener which is able to resolve the targetEntity during runtime and targetEntity can be set to an Abstract Class or an Interface:
/**
* #ORM\ManyToOne(targetEntity="Acme\InvoiceBundle\Model\InvoiceSubjectInterface")
* #var InvoiceSubjectInterface
*/
protected $subject;
InvoiceSubjectInterface will then be replaced during runtime by a specific class provided by config e.g.:
# app/config/config.yml
doctrine:
# ...
orm:
# ...
resolve_target_entities:
Acme\InvoiceBundle\Model\InvoiceSubjectInterface: AppBundle\Entity\Customer
So this should be eiter an extendable behaviour for providing no class or implementing an own solution.
Related
I need to create an like/dislike system which can be used on any entity. I'm going to create a Like entity with an 'Id', 'Entity' (can be anything), 'author' (ManyToOne with User class) and 'like' (boolean).
I just want to know if there is a good way to do it ?
I can't use the table inheritance (mappedsuperclass) because this entity will be part of a bundle (SocialBundle) which can be used on several project (It will be a vendor).
I have no code to show you because i'm still in the analysis part.
Thanks !
Create an interface for that entity and later you can map this interface to any entity using addResolveTargetEntity method. See this.
I'm working on an events site and have a one to many relationship between a production and its performances, when I have a performance object if I need its production id at the moment I have to do
$productionId = $performance->getProduction()->getId();
In cases when I literally just need the production id it seems like a waste to send off another database query to get a value that's already in the object somewhere.
Is there a way round this?
Edit 2013.02.17:
What I wrote below is no longer true. You don't have to do anything in the scenario outlined in the question, because Doctrine is clever enough to load the id fields into related entities, so the proxy objects will already contain the id, and it will not issue another call to the database.
Outdated answer below:
It is possible, but it is unadvised.
The reason behind that, is Doctrine tries to truly adhere to the principle that your entities should form an object graph, where the foreign keys have no place, because they are just "artifacts", that come from the way relational databases work.
You should rewrite the association to be
eager loaded, if you always need the related entity
write a DQL query (preferably on a Repository) to fetch-join the related entity
let it lazy-load the related entity by calling a getter on it
If you are not convinced, and really want to avoid all of the above, there are two ways (that I know of), to get the id of a related object, without triggering a load, and without resorting to tricks like reflection and serialization:
If you already have the object in hand, you can retrieve the inner UnitOfWork object that Doctrine uses internally, and use it's getEntityIdentifier() method, passing it the unloaded entity (the proxy object). It will return you the id, without triggering the lazy-load.
Assuming you have many-to-one relation, with multiple articles belonging to a category:
$articleId = 1;
$article = $em->find('Article', $articleId);
$categoryId = $em->getUnitOfWork()->getEntityIdentifier($article->getCategory());
Coming 2.2, you will be able to use the IDENTITY DQL function, to select just a foreign key, like this:
SELECT IDENTITY(u.Group) AS group_id FROM User u WHERE u.id = ?0
It is already committed to the development versions.
Still, you should really try to stick to one of the "correct" methods.
So I'm working on some unit tests and relational fixtures.
I'm creating a model dynamically like:
$model = CActiveRecord::model('Post');
$post = $model->findByPk(1);
But after that I cannot for some reason get $post->id. I traced the problem to CActiveRecord class:
public function __get($name)
{
if(isset($this->_attributes[$name]))
return $this->_attributes[$name];
...
Where $name = "id". It says that $this->_attributes[$name] does not exist! As a matter of fact _attributes is empty.
My Post class does not define id (or any other properties) as a public property and I don't want to do so either. I just let the AR map it to table columns for me.
What am I missing?
Edit 1
My fixtures are regular Yii fixtures - nothing really special about them.
What differs is the way I load them really. I extended the CDbFixtureManager to be able to specify the order in which they should be loaded by overloading load() method. Only thing of interest that actually fails is that in the fixtures that have foreign keys I use the following:
'comment1' => array('post_id' => $this->getRecord('Post', 'post1')->id);
That's where it fails. getRecord returns the actual Post record (since I know the Post fixture has already been successfully loaded and exists in DB), but on the ->id part I get an exception about that attribute not existing.
If I go into Post model and add public $id; to it, then everything works! But I'm not sure if it's good practice to go about declaring all properties public like that.
If you look at this page carefully:
http://www.yiiframework.com/doc/guide/1.1/en/test.unit
you'll see that they use an array form for retrieving fixtures:
$this->posts['sample1']['id']
There is an alias defined in their fixture array for each record and fixture items aren't loaded as models really ...
Does that help? If not, it would be helpful to see your fixture file :-)
I think I found the root cause of this issue for me. While my FixtureManager was using the testdb DBConnection, the models still used the regular one.
For whatever reason, my debugger was giving me misleading errors like the one described in my original post.
Once I was able to set the DBConnection of all Models in the unit test the puzzle snapped into place and everything is now working smoothly!
I am using OpenJPA (JPA 1.0) on WebLogic 10.0.x with Oracle. I have defined a OneToMany relationship as below:
#Entity
public class Compound implements Serializable {
...
#OneToMany(mappedBy="compound", fetch=FetchType.LAZY, cascade=CascadeType.ALL)
private List<Submission> submissions = new ArrayList<Submission>();
...
}
#Entity
public class Submission implements Serializable {
...
#ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.REFRESH)
#JoinColumn(name="compoundId")
private Compound compound;
...
}
When I delete a Compound entity all child Submission entities should be deleted also. This works as a general rule, except that I have a foreign key constraint setup on these tables:
ALTER TABLE SUBMISSION
ADD CONSTRAINT FK_SUBMISSION_COMPOUND
FOREIGN KEY (COMPOUNDID)
REFERENCES COMPOUND(COMPOUNDID);
Now when I attempt to delete the Compound entity I encounter the following exception:
ORA-02292: integrity constraint (HELC.FK_SUBMISSION_COMPOUND) violated - child record found {prepstmnt 3740 DELETE FROM Compound WHERE compoundId = ? [params=(long) 10384]} [code=2292, state=23000]"
The above exception implies that Open JPA is attempting to delete the parent prior to cascading the delete onto the child entities. I've read a few articles via Google about this exception, dating back to 2006. However, the most recent article suggests that this bug has been fixed?
http://mail-archives.apache.org/mod_mbox/openjpa-dev/200609.mbox/%3C14156901.1158019042738.JavaMail.jira#brutus%3E
https://issues.apache.org/jira/browse/OPENJPA-235
Can anyone suggest why this is not working and what I can do about it? I am loathe to manually delete the child entities, especially as this is one of the less-complicated relationships in my schema and whatever solution I use for this I will need to apply elsewhere.
Thanks
Jay
When I delete a Compound entity all child Submission entities should
be deleted also. This works as a general rule, except that I have a
foreign key constraint setup on these tables:
If you can change the foreign key constraint, that should solve the problem as far as the database is concerned. I'm not sure how OpenJPA will behave here.
ALTER TABLE SUBMISSION
ADD CONSTRAINT FK_SUBMISSION_COMPOUND
FOREIGN KEY (COMPOUNDID)
REFERENCES COMPOUND(COMPOUNDID)
ON DELETE CASCADE;
One thing - as discussed above this is Weblogic 10.0.x. I suspect we
are using the bundled version of OpenJPA / Kodo, which is probably
quite old...
My own feeling is that the bug you referred to should have been fixed by this version, but it's also a) close enough in time that it might not have been fixed, and b) potentially a big enough problem that I think you should spend some time verifying the version and fix. (Actually, I just noticed that OpenJPA 1.0 was released on Aug 2007. That's a lot earlier than I thought, which makes it more likely you don't have the bug fix.)
If you can't modify the database (because it's a legacy system that clearly doesn't intend for clients to rely on cascading deletes), and if the bug isn't fixed in your version, you'll have to manage the order of SQL statements yourself.
The burden of manually managing SQL statements--which is one of the things that OpenJPA is supposed to do for you--might be enough to get management to either upgrade OpenJPA or to update the foreign key constraints in the database.
I really hope you get a better answer than this one.
I created yaml configuration for Doctrine. When I'm trying doctrine orm:generate-entities, it creates php files with getters and setters in camel case. So, is_public field transforms into setIsPublic and getIsPublic methods. It's owful. How can I get set_is_public and get_is_public? I can manually edit generated php files, but I don't know what will happen when I change the schema.
You can choose a naming strategy that Doctrine will use to generate the items using:
Using a naming strategy you can provide rules for automatically
generating database identifiers, columns and tables names when the
table/column name is not given. This feature helps reduce the
verbosity of the mapping document, eliminating repetitive noise (eg:
TABLE_).
For your specific case, I think you're looking at something like:
$namingStrategy = new \Doctrine\ORM\Mapping\UnderscoreNamingStrategy(CASE_LOWER);
$configuration()->setNamingStrategy($namingStrategy);
The linked topic goes on to show you how you can write your own custom naming strategy.
If you're using Symfony, it's even easier (like most things are with Symfony, but that's just my opinion) via config.yml:
doctrine:
orm:
naming_strategy: doctrine.orm.naming_strategy.underscore
Symfony's coding standards encourage Symfony users to use camelCase:
Naming Conventions
Use camelCase, not underscores, for variable,
function and method names, arguments
Personal advice - do not generate entities by doctrine orm:generate-entities.
Use plain PHP to create class. Why?
Orm uses reflection on privates to communicate with database. You dont need to generate setters and getters. I recomend You to use design patterns such as factory or constructor to achive Your goal. Decorators also should work fine.
<?php
class MyClass
{
private $id;
private $name;
public function __construct(int $id, string $name)
{
$this->id = $id;
$this->name = $name;
}
}
$camelCase is not only Symfony's recomendation for code standard. It's based on PSR2. I highly recomend using PSR2, code gets clean and standarized.
Standard ORM naming strategy is $camelCase private var to snake_case column name. If you want to change it otherwise, consider: other naming stategies