I have simple table inheritance on my entities, say something like:
/**
* #InheritanceType("SINGLE_TABLE")
* #DiscriminatorColumn(name="type", type="string")
* #DiscriminatorMap({"base"="BaseArticle", "extended"="ExtendedArticle"})
*/
class BaseArticle extends \Models\BaseModel{
...
}
class ExtendedArticle extends BaseArticle{
/**
* #column(type="string")
*/
protected $extendedProperty;
}
I need to do a query across all article types, but in some types restrict the query by some property, that is in extended ExtendedArticle, ie:
SELECT a FROM BaseArticle a WHERE (a INSTANCE OF BaseAricle) OR (a INSTANCE OF ExtendedArticle AND a.extendedProperty = "xy")
Which gives me following exception:
[Semantical Error] line 0, col 406 near 'extendedProperty="xy"))': Error: Class Models\Articles\BaseArticle has no field or association named location
So question is, how to access child's properties in query to parent class?
You can't. Here's a workaround:
SELECT a
FROM BaseArticle a
WHERE
a INSTANCE OF BaseAricle
OR a.id IN (
SELECT ea.id
FROM ExtendedArticle ea
WHERE ea.extendedProperty = "xy"
)
I have solved this by adding $extendedProperty into BaseArticle and using getters/setters to effectively hide this property in my BaseArticle entity and lifecycle callbacks to handle NULLABLE. But this is really bad solution as the inheritance is almost pointless.
Related
This is classic method for the entity relationship;
/**
* #ORM\OneToMany(targetEntity="Product", mappedBy="category")
*/
protected $products;
and other entity;
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="products")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
protected $category;
But, when I use this method, I can't use limit function. All results are taken. But, I should use sql limit function. So, I use a repository class. I wrote all sql code again. I used join function again. Is the relationsip annotation necessary in this situation? Do I still need to add this annotation?
Unfortunately, there is no way of doing what are you asking. You are gonna have to write a custom DQL, preferably inside a custom repository in order to fetch exactly what you want. Of course you still need the annotation itself, as is required by Doctrine to do many things under the hood and, in general, to keep a good relationship model between your entities.
The most close annotation you have to control the way in which relationships are joined when using automatic method is the OrderBy one:
/*
* #OneToMany(...)
* #OrderBy({"name" = "ASC"})
*/
protected $relation;
Below is what i want:
if("phone" property exists in entity){
//get value
"phone"=>$entity->getProperty("phone")->getValue();
}
cant find anything on azure page.
Use the DynamicTableEntity class to do that. When you retrieve entities from a table without the generic TableEntity class, your entities will be of type DynamicTableEntity. This class has a property class Properties, which is of type IDictionary. So you should have code like this (I don't know if this compiles, just writing it from the top of my head):
if(entity.Properties.Keys.Contains("phone"))
{
var x = entity.Properties["phone"].toString() //Or cast it to whatever you need
}
I am using JMSSerializerBundle to serialize my entities to json and deserialize json into entities, but I think this question applies for any deserialization techniques.
For example, this schema:
class Order
{
private $id;
/**
* #Serializer\Type("ArrayCollection<MyBundle\Entity\Order\Item>")
* #ORM\OneToMany(targetEntity="\MyBundle\Entity\Order\Item", mappedBy="order", cascade={"persist"})
*/
private $items;
}
class Item
{
private $id;
/**
* #ORM\ManyToOne(targetEntity="\MyBundle\Entity\Order", inversedBy="items")
*/
private $order;
/**
* #var integer $amount
* #Serializer\Type("integer")
* #ORM\Column(name="amount", type="integer")
*/
private $amount;
}
Maps to this json: {"id":1,"items":[{"id":1,"amount":100}, {"id":2,"amount":200}]} and the same json is properly deserialized into an object of type MyBundle:Order that has a colletion of two MyBundle:Order/Item objects.
The problem is that when I try to persist this object, new entries are created in the database, rather than updating existing, ignoring the ids. How do I tell entity manager that theses objects should be updated, rather that created?
Update. Generally EntityManager::merge solution (as suggested by DaveM) is fine. But you must only merge existing objects. For example, if you have a json that represents a new Order entity that is connected to existing Order\Item entities
{"id":null,"items":[{"id":1,"amount":100}, {"id":2,"amount":200}]}
In this case you cannot just merge an Order object like this:
$em->merge($order), because order is a new entity and entity manager will attempt to find an Order object with id = null and you will end up with a new Order and empty items array. So the solution is to loop the Order::$items array and merge each item individually. Then a new order will be created and connected with existing items.
You need to use the merge() method on the EntityManager as merging entities refers to the merging of entities into the context of an EntityManager so that they can become managed again. In order to merge the state of an entity into an EntityManager use the EntityManager#merge($entity) method. The state of the passed entity will be merged into a managed copy of this entity and this copy will subsequently be returned.
$detachedEntity = unserialize($serializedEntity);
$entity = $em->merge($detachedEntity);
Also be sure to note when you want to serialize/unserialize entities you have to make all entity properties protected, never private. The reason for this is, if you serialize a class that was a proxy instance before, the private variables won’t be serialized and a PHP Notice is thrown.
More information can be found in the doctrine documentation here:
http://doctrine-orm.readthedocs.org/en/2.0.x/reference/working-with-objects.html#merging-entities
I know this question is three years old, but it mislead me to think the only answer was using the merge operation. I'd like to add my two cents:
The JMSSerializerBundle includes an object constructor for Doctrine entities. When you enable this constructor, the deserialized entities are managed entities that can be persisted(with $em->persist($entity)).
Please check this comment to understand other benefits from this.
And here is how you can enable it.
I have a class:
class abstract Car {
...
}
class ConcreteCar {
...
}
class AnotherConcreteCar {
...
}
Yet it seems very confusing as to how to map these in NHibernate. I do need to be able to gather a:
List<Car> cars;
for (Car car in cars) {
...
}
I also would like to use a generator (what is the alternative?) for generating the next carID. There is join-subclass, which disallows that, and frankly, it is all confusing me. It seems I will have to go back to making Car an interface, which isn't really what I want.
Also, no matter what method I use (say I just make Car a superclass and the other subclasses), if I query the database:
from item in session.Query<Car>()
select item;
will I have to typecast the objects into their subclass types to use the subclass properties and methods? Will that work? Does NHibernate actually figure out the subclass and create objects of the subclass, or does it just create objects of the superclass that cannot be converted into their subclasses?
I've implemented this kind of approach already a while ago but I remember it was working in my particular case.
I assume that Car is the base table with some shared columns, if so then you could map your entities in the following way (I'm using Fluent NHibernate):
CarMap:
public class CarMap : ClassMap<Car> { ... }
ConcreteCarMap:
public class ConcreteCarMap : SubclassMap<ConcreteCar>
{
public ConcreteCarMap()
{
Extends<CarMap>();
// other properties
// Map(x => x.Property).Not.Nullable();
}
}
Having that you could execute your query:
from item in session.Query<Car>() select item;
And indeed you would have to typecast returned objects into their subclass types in order to access the subclass properties and methods but NHibernate would be intelligent enough to construct them properly for you.
NHibernate would achieve that using this kind of syntax (pseudo sql):
select
-- all columns from Car
-- all columns from ConcreteCar
-- other columns from subclasses
case
when f1.SubId is not null then 1
when f2.SubId is not null then 2
when f3.SubId is not null then 3
when f4.SubId is not null then 4
when f5.SubId is not null then 5
when f0.Id is not null then 0
end as type
from Car f0
left outer join ConcreteCar f1 on f0.Id = f1.SubId
-- other joins
So depending on the actual number of implemented subclasses this can have an impact on the performance.
I hope this answers your question.
it's quite possible.
see inheritance mapping.
You Can use session.Query<Car> and nHib will know to create objects of the appropriate subclass.
let me know if you have any more questions.
Yes it works!
This is my code:
public abstract class Vote:EntityBase
public class VoteComment:Vote,IVote
public class VotePhoto:Vote,IVote
internal List<Vote> Test()
{
return session.Query<Vote>().ToList();
}
and this is the result:
Using NHibernate; is it possible to query against a super class while performing restrictions at the subclass level?
For example (appologies for the psuedo-code):
Class A
Property Prop1
End Class
Class B
Inherits Class A
Property Prop2
End Class
Class C
Inherits Class A
Property Prop3
End Class
How would I perform a query as follows:
from A where Prop1 = 'foo' AND
((if A is B) then B.Prop2 = 'bar' OR
(if A is C) then C.Prop3 = 'bar')
Is something like this possible using Nhibernate.Linq? What about hql or the criteria API?
Since HQL and ICriteria are Domain-query tools i find it hard to expect such a functionality.
AFAIK the only close thing is using ICriteria's Expression.Sql() where you query the superclass but inject subclass specific fragments with pure sql. There, the expression would be like
crit.Add(
Expression.Sql(#"(({alias}.DiscrimCol = :subClassADiscrimVal AND {alias}.Col2 = :barVal)
OR ({alias}.DiscrimCol = :subClassBDiscrimVal AND {alias}.Col3 = :barVal))",
new object[] { "subA", "subB", "bar" },
new IType[] { NHibernateUtil.String,NHibernateUtil.String,NHibernateUtil.String } )
);
not nice but it works
The other way would be using an ISQLQuery which at least allows the SELECT part to be domain specific (and using .AddEntity()) so you can still select your managed superclass and the WHERE part contains subclass specific fragments
========= UPDATE ==========
On a second thought, there is a way to implement this via HQL or ICriteria but it more of a workaround and is less performant because it involves subqueries. example in ICriteria:
nhSes.CreateCriteria(typeof(Super))
.Add(
Restrictions.Disjunction()
.Add(Subqueries.PropertyIn("Id", DetachedCriteria.For(typeof(ChildA))
.SetProjection(Projections.Id())
.Add(Restrictions.Eq("ChildAProp", barVal))))
.Add(Subqueries.PropertyIn("Id", DetachedCriteria.For(typeof(ChildB))
.SetProjection(Projections.Id())
.Add(Restrictions.Eq("ChildBProp", barVal))))
)
i am quering each child, project its id and then select the super class, limiting the ids with IN from the projected child ids.