There are some entities (Region, Country, City) which used STI (or even CTI). How it possible convert Country to City leaving old id?
This is not supported in Doctrine 2 because type casting user objects is not supported by PHP.
With that said, Doctrine uses the discriminator column to determine what type of object to hydrate. If you change the value of the discriminator column in the database with a SQL UPDATE, the object type will be changed the next time the object is loaded. This works for STI, but CTI would be more complicated.
It may not be possible by standard using Doctrine, but you can work around it.
If you use the Class Metadata you can select your discriminator column.
Take a look at the Trait that I've created to solve the problem within my app:
namespace App\Doctrine\Repository;
trait DiscriminatorTrait
{
abstract public function getClassMetadata();
abstract public function getEntityManager();
private function updateDiscriminatorColumn($id, $class)
{
$classMetadata = $this->getClassMetadata();
if (!in_array($class, $classMetadata->discriminatorMap)) {
throw new \Exception("invalid discriminator class: " . $class);
}
$identifier = $classMetadata->fieldMappings[$classMetadata->identifier[0]]["columnName"];
$column = $classMetadata->discriminatorColumn["fieldName"];
$value = array_search($class, $classMetadata->discriminatorMap);
$connection = $this->getEntityManager()->getConnection();
$connection->update(
$classMetadata->table["name"],
[$column => $value],
[$identifier => $id]
);
}
}
I do have to warn you though, when your sub-classes have (a lot of) extra fields you will need to fill or clear them manually afterwards.
Related
What I'm trying to do is basically Entity A which have field type to relate with Entity B or C base on the value of field type
for example: a Product if is type drinks to be related with DrinkOptions entity, but if it's with type food to be related with FoodOptions entity
Is there any elegant solution which can work fine in such case?
This is what I thought it will work, but it doesn't..
Changing dynamically the entity A relation will not help, because it's not yet loaded and I can't see the value of field type
combine the different option entities into one, but it will be very ugly picture with 200+ properties..
define all option types in the config and then dynamically create the mapping with the loadClassMetadata, but is not so pretty also it requires a "dummy" property, getters and setters for all option types in the Entity A
A possible solutions could be to use a single table inheritance. Might not be the nicest solution but it could fit your specific need.
<?php
namespace MyProject\Model;
/**
* #Entity
* #InheritanceType("SINGLE_TABLE")
* #DiscriminatorColumn(name="discriminator", type="string")
* #DiscriminatorMap({"drink" = "DrinkOption", "food" = "FoodOption"})
*/
class Option
{
// ...
}
/**
* #Entity
*/
class FoodOption extends Option
{
// ...
}
/**
* #Entity
*/
class DrinkOption extends Option
{
// ...
}
You then could even switch the type by altering the "discriminator" column.
I think the simpliest way would be to have multiple product entities with an inheritence
Like Product (parent) -> foodProduct (child) or DrinkProduct().
This way you could have Product link to common others entities (like price or brand) and specific link for food, drinks, smokes etc...
Product
id:
type: food/drink/smoke etc..
foodproduct: one to Many nullable: FoodProduct (it's actually one to zero/one but i find it more performant)
drinkproduct: one to Many nullable: DrinkProduct
smokeproduct:one to Many nullable: SmokeProduct
FoodProduct:
id:
product_id (many to one to Product)
options one to many to foodOptions
If it still don't fit you will have to find some design pattern
I have a Product entity and a Shop entity.
A shop can have 0 to n products and a product can be in only one shop.
The product entity table is thus refering to the Shop entity through a shop_id table field.
When querying for the products of a given shop using doctrine query builder, we can do this:
$products = $this->getDoctrine()->getRepository('MyBundle:Product')
->createQueryBuilder('p')
->where('p.shop = :shop')
->setParameter('shop', $shop) // here we pass a shop object
->getQuery()->getResult();
or this:
$products = $this->getDoctrine()->getRepository('MyBundle:Product')
->createQueryBuilder('p')
->where('p.shop = :shopId')
->setParameter('shopId', $shopId) // we pass directly the shop id
->getQuery()->getResult();
And the both seem to work... I'm thus wondering: can we always pass directly entity ids instead of entity instances in such cases (ie: on a doctrine entity field that refers to another entity)?
I initially thought that only the first example would work...
According to the Doctrine documentation, you can pass the object to setParameter() if the object is managed.
extract from the documentation:
Calling setParameter() automatically infers which type you are setting as value. This works for integers, arrays of strings/integers, DateTime instances and for managed entities.
for more information, please see:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/query-builder.html#binding-parameters-to-your-query
I might be wrong, but I think that if you pass object instance instead of id number, Doctrine will automatically call $instance->getId() making your two queries the same when translated into SQL (even DQL).
When you sort on column a descending, it will put all records having a NULL at the bottom, which makes sense. What I want is the following ordering when I'm sorting on attribute a descending:
a
----
NULL
NULL
5
4
3
2
1
I know Oracle has NULLS_FIRST and in MySQL you could use ORDER BY ISNULL(a), but I'm wondering whether there is an elegant way to deal with this using Doctrine2 functionality only? So not using native queries, etc.
Answer with the help of S0pa: I fixed it using the following new function node for Doctrine2:
<?php
namespace MyBundle\General;
use \Doctrine\ORM\Query\Lexer;
class IsnullFunctionNode extends \Doctrine\ORM\Query\AST\Functions\FunctionNode
{
private $isnull;
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->isnull = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
return 'ISNULL('.$this->isnull->dispatch($sqlWalker).')';
}
}
And connected it in Symfony2 to Doctrine2 like this (app.yml):
doctrine:
orm:
entity_managers:
default:
...
dql:
numeric_functions:
isnull: Diagenda\CommonBundle\General\IsnullFunctionNode
I can't use these functions in my ORDER BY clause, but by doing a SELECT ISNULL(a) AS n_a I can order on the n_a attribute.
I think the best way is to define your own DQL function, using this tutorial. You will still need to handle different case depending on your DB management system.
I have an entity 'Order', and a one-to-many associated entity 'OrderStatus' (so 1 order can have many statuses). The current status of an order is defined by the last status that was added to that order.
Now I want to create a DQL query which selects all orders with a certain status. However, because this is a one-to-many relation, I have no idea how to accomplish this in DQL. I only know of querying the collection of statuses as a whole.
Does anyone have an idea if this is even possible, or do I have to use a workaround?
We had this very same problem in a project we're working on. The query you're trying to perform is very similar to this question, except that you're trying to do this in DQL, which makes it even harder. I think (but I might be wrong) that DQL does not allow this kind of query, and you may achieve the result you're expecting with a native SQL query, with all the caveats this implies.
What we ended up with, and I strongly suggest to you, is to make the current status a property of your Order. This allows easy & fast querying, with no joins required. The change is really painless:
class Order
{
/**
* #ManyToOne(targetEntity="OrderStatus")
*/
protected $status;
/**
* #OneToMany(targetEntity="OrderStatus")
*/
protected $statuses;
public function setStatus($status)
{
$orderStatus = new OrderStatus($this, $status);
$this->statuses->add($orderStatus);
$this->status = $orderStatus;
}
}
$status can also be a simple string property, if your OrderStatus is basically composed of a status string and a date; the code would then become:
class Order
{
/**
* #Column(type="string")
*/
protected $status;
// ...
public function setStatus($status)
{
// ...
$this->status = $status;
}
}
I need to take the results of a query:
var query = from m in db.SoilSamplingSubJobs where m.order_id == id select m;
and prepare as an ICollection so that I can have something like
ICollection<SoilSamplingSubJob> subjobs
at the moment I create a list, which isnt appropriate to my needs:
query.ToList();
what do I do - is it query.ToIcollection() ?
List is an ICollection. You can change you query.ToList() code to the following.
query.ToList() as ICollection<SoilSamplingSubJob>;
You question sounds like this query is returned as a function result. If this is the case remember that the Linq to SQL objects are connected by default so you will need to manage where your database context get opened and closed. Alternatively you can create DTOs (Data Transfer Objects) that hold the data you want to use in the rest of your program. These objects can fit into your object hierarchy any way you want.
You can also create these DTOs as part of the query.
var query = from m in db.SoilSamplingSubJobs where m.order_id == id
select new SubJobDTO {
OrderNumber = m.order_id
};
return query.ToList() as ICollection<SubJobDTO>;
Since, query implements IEnumerable, you can pass it to the constructor of the collection of your choice. ICollection is an interface, implemented by several classes (including List<T>, which ToList returns) with different performance characteristics.
With slightly different syntax, you can do something like this as well that can cut down on what goes into the ICollection. This type of approach is great for Angular and MVC when you have a huge table but only want to load some props into the cshtml page:
ICollection<SoilSamplingSubJob> samples = dbContext.GetQuery().Where(m => m.order_id == id)
.AsEnumerable().Select(s => new
{
Id = s.order_id,
CustomProperty = s.some_thing
}).ToList() as ICollection<SoilSamplingSubJob>