Doctrine 2 left join with in a tree - doctrine-orm

I have two entities: People and Document
A people can have n childrens:
/**
*
* #ORM\ManyToOne(targetEntity="People")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="dad_id", referencedColumnName="id")
* })
*/
private $dad;
/**
*
* #ORM\OneToMany(targetEntity="People", mappedBy="dad")
*/
private $childrens;
And a people can have n Documents:
DocumentEntity:
/**
*
* #ORM\ManyToOne(targetEntity="People")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="people_id", referencedColumnName="id")
* })
*/
private $people;
I want to get all peoples (and their children) and their documents of type License in one query:
$qb = $this->em->getRepository('People')->createQueryBuilder('p');
$qb
->select(array('p','d'))
->leftJoin('p.documents','d','WITH','d.type = :type')
->setParameter('type','license')
;
$peoples = $qb->getQuery()->getResult();
Fine, if i call $people->getDocuments() returns only Documents of type 'License'. But when i do this '$people->getChildrens()[0]->getDocuments()' returns for me all Documents of this people.
How i can do a 'left join WITH' cascading on a tree structure?

Related

Doctrine ORM insert auto increment ID to other column on same insert

In my table the id is auto increment. And cid and dsid can not be NULL. On insert i need somehow to insert in dsid the value of the auto increment cid. How can i manage this in doctrine ORM / Symfony?
Table:
--------------------------------------
cid | dsid | lid
--------------------------------------
Entity:
/**
* #var int
*
* #ORM\Column(name="CID", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $cid;
/**
* #var int
*
* #ORM\Column(name="DSID", type="integer", nullable=false)
*/
private $dsid;
/**
* #var int
*
* #ORM\Column(name="LID", type="integer", nullable=false)
*/
private $lid;
Id generation takes place on database side during inserting, it has nothing to do with Doctrine. You can implement trigger on database side. or make fields nullable to insert a record, and then have postFlush event that will set those fields equal to ID.

Modify formula stored in string via VBA

(I have hundreds of rows need to change. The data is like this
activity_id equation
5225518 D0312_ABC * (S3765+S3790+S3762+S3763+S3770+S3764+S4480) * (1-(S2820+S0560))*(1-S1965)*(1-C0151)
7306234 D0300_BCD * C0502 * (1-(S0191))*(1-S1965)
8293425 D0798_CDE * P0692 * (1-(S0191+S2820+S0560+S0290+S0960))*(1-S1965)
9119429 D0793_DEF * S2605 * (1-S0290)*(1-S1965)
I need to combine the two "1-something" together and there is a pattern.
When ever there is a (1-(Sxxx+Sxxxx))*(1-(Sxxx+Sxxxx)),
combine them into one which is (1-(Sxxx+Sxxxx +Sxxx+Sxxxx))
So I only need to change everything with Sxxxx, you can ignore Cxxxx, Dxxxx, Pxxxx, ...
SO I need to change it to:
activity_id equation
5225518 D0312_ABC * (S3765+S3790+S3762+S3763+S3770+S3764+S4480) * (1-(S2820+S0560+S1965))*(1-C0151)
7306234 D0300_BCD * C0502 * (1-(S0191+S1965))
8293425 D0798_CDE * P0692 * (1-(S0191+S2820+S0560+S0290+S0960+S1965))
9119429 D0793_DEF * S2605 * (1-(S0290+S1965))
The following regexp certainly may and arguably should be improved.
Option Explicit
Private m_Rex As RegExp
Private Const SEARCH_PATTERN As String = "\(1-\(?((S\d{4}\+?)+)\)?\)\*\(1-\(?((S\d{4}\+?)+)\)?\)"
' $0 $2
Private Const REPLACE_PATTERN As String = "(1-($1+$3))"
Public Function Simplify(ByVal AVeryParticularFormula As String) As String
If m_Rex Is Nothing Then
Set m_Rex = New RegExp
m_Rex.Global = False
m_Rex.MultiLine = False
m_Rex.IgnoreCase = False
m_Rex.Pattern = SEARCH_PATTERN
End If
Do
Simplify = m_Rex.Replace(AVeryParticularFormula, REPLACE_PATTERN)
If Simplify = AVeryParticularFormula Then Exit Do
AVeryParticularFormula = Simplify
Loop
End Function
? Simplify("D0312_ABC * (S3765+S3790+S3762+S3763+S3770+S3764+S4480) * (1-(S2820+S0560))*(1-S1965)*(1-C0151)")
D0312_ABC * (S3765+S3790+S3762+S3763+S3770+S3764+S4480) * (1-(S2820+S0560+S1965))*(1-C0151)
? Simplify("D0300_BCD * C0502 * (1-(S0191))*(1-S1965)")
D0300_BCD * C0502 * (1-(S0191+S1965))
? Simplify("D0798_CDE * P0692 * (1-(S0191+S2820+S0560+S0290+S0960))*(1-S1965)")
D0798_CDE * P0692 * (1-(S0191+S2820+S0560+S0290+S0960+S1965))
? Simplify("D0793_DEF * S2605 * (1-S0290)*(1-S1965)")
D0793_DEF * S2605 * (1-(S0290+S1965))

Updating association

I have two entities with a many-to-many relationship (Scenario and Step). I needed to save the order of the steps within a scenario so i added a third entity called ScenarioToStep with a displayOrder property.
class ScenarioToStep
{
/**
* #ORM\Id
*
* #ORM\ManyToOne(targetEntity="Hakisa\Bundle\GettingStartedBundle\Entity\Scenario", inversedBy="scenarioToStep")
* #ORM\JoinColumn(name="scenario_id", referencedColumnName="id")
*
* #var Hakisa\Bundle\GettingStartedBundle\Entity\Scenario
*/
private $scenario;
/**
* #ORM\Id
*
* #ORM\ManyToOne(targetEntity="Hakisa\Bundle\GettingStartedBundle\Entity\Step", inversedBy="stepToScenario")
* #ORM\JoinColumn(name="step_id", referencedColumnName="id")
*
* #var Hakisa\Bundle\GettingStartedBundle\Entity\Step
*/
private $step;
/**
* Order of the step in the scenario
*
* #ORM\Id
*
* #ORM\Column(type="integer")
*
* #var integer
*/
private $displayOrder;
The 3 properties makes the primary key so i can have a step multiple times in a scenario
I have a UI where i can add steps via drag n drop, remove steps or sort step. This UI send an array to PHP where the index is the position and the value is the id of the step.
array(
0 => 1,
1 => 2,
2 => 3,
3 => 1,
4 => 4
)
I want to update my Scenario entity to match this new ordering. I first tried something like:
$scenario->getScenarioToStep()->clear();
foreach ($tabIds as $pos => $stepId) {
$sts = new ScenarioToStep();
$sts->setScenario($scenario)->setStep($step)->setOrder($pos);
$scenario->getScenarioToStep()->add($sts);
}
but i end up with duplicate key errors
if i do similar thing but instead of instanciating a new scenario i ask entity manager for a reference (proxy) then the association table is always empty
Thanks to anyone who has an idea on how i should perform my save

doctrine 2 single table inheritance in a many to one relationship

I'm working with a Legacy database (osCommerce-based and heavily hacked), which contains a table called orders_total, defined as follows:
CREATE TABLE `orders_total` (
`orders_total_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`orders_id` int(11) unsigned NOT NULL DEFAULT '0',
`title` varchar(255) NOT NULL,
`text` varchar(255) NOT NULL,
`value` decimal(15,4) NOT NULL DEFAULT '0.0000',
`class` varchar(40) NOT NULL,
`sort_order` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`orders_total_id`),
KEY `idx_order_id` (`orders_id`)
);
The "class" column defines the type of "total" that the row represents. It's a bit of a mess in that some "totals" are not totals at all, but surcharges, and some are discounts. An example:
+-----------------+-----------+--------------+----------+-----------+----------------------+------------+
| orders_total_id | orders_id | title | text | value | class | sort_order |
+-----------------+-----------+--------------+----------+-----------+----------------------+------------+
| 781797 | 190000 | Sub-Total: | $1427.29 | 1427.2916 | ot_subtotal | 1 |
| 781798 | 190000 | Courier: | $172.05 | 172.0500 | ot_shipping | 2 |
| 781799 | 190000 | Insurance: | $47.62 | 47.6200 | ot_insurance | 3 |
| 781800 | 190000 | Visa/MC Fee: | $41.80 | 41.8000 | ot_surcharge_visa_mc | 4 |
| 781801 | 190000 | <b>Total:</b>| $1688.76 | 1688.7616 | ot_total | 10 |
+-----------------+-----------+--------------+----------+-----------+----------------------+------------+
What I'd like to do is use single table inheritance to split out the table classes into OrderTotal, OrderCharge and OrderDiscount entities, and then through one to many relationships with the Order entity be able to pull up each collection. I've tried the following:
<?php
namespace Shop\Entity;
use Application\Entity\BaseEntity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="class", type="string")
* #ORM\DiscriminatorMap({
* "ot_cc_surcharge" = "OrderCharge",
* "ot_coupon" = "OrderDiscount",
* "ot_insurance" = "OrderCharge",
* "ot_payment_surcharge" = "OrderCharge",
* "ot_paypal_surcharge" = "OrderCharge",
* "ot_shipping" = "OrderCharge",
* "ot_subtotal" = "OrderTotal",
* "ot_surcharge" = "OrderCharge",
* "ot_surcharge_amex" = "OrderCharge",
* "ot_surcharge_paypal" = "OrderCharge",
* "ot_surcharge_visa_mc" = "OrderCharge",
* "ot_total" = "OrderTotal"
* })
* #ORM\Table(name="orders_total")
*
*/
class OrderTotalBase extends BaseEntity {
/*....*/
}
/**
* #ORM\Entity
* #ORM\Table(name="orders_total")
*/
class OrderCharge extends OrderTotalBase {
}
/**
* #ORM\Entity
* #ORM\Table(name="orders_total")
*/
class OrderTotal extends OrderTotalBase {
}
/**
* #ORM\Entity
* #ORM\Table(name="orders_total")
*/
class OrderDiscount extends OrderTotalBase {
}
/*....*/
class Order {
/*....*/
/**
* #ORM\OneToMany(targetEntity="OrderTotal", mappedBy="order", fetch="EAGER")
* #var PersistentCollection
*/
protected $orderTotals;
/**
* #ORM\OneToMany(targetEntity="OrderCharge", mappedBy="order", fetch="EAGER")
* #var PersistentCollection
*/
protected $orderCharges;
/**
* #ORM\OneToMany(targetEntity="OrderDiscount", mappedBy="order", fetch="EAGER")
* #var PersistentCollection
*/
protected $orderDiscounts;
}
Then added the respective getters and setters to the Order class.
Problem is, when I call $order->getOrderCharges(), it returns the entire list of records above all as OrderCharge objects. When I call $order->getOrderTotals() I get exactly the same results but with OrderTotal objects.
Does STI allow me to only get the correct objects according to the #DiscriminatorMap?
While (as explained above) your mappings are invalid, there is a way of fetching all entities in an inheritance according to the discriminator column in DQL:
SELECT r FROM RootEntity r WHERE r INSTANCE OF SubType
Or simply select the correct type with DQL:
SELECT e FROM SubType e
For your OrderTotal case, the association should be
/**
* #ORM\OneToMany(targetEntity="OrderTotal", mappedBy="totalOrder")
* #var \Doctrine\Common\Collections\Collection
*/
protected $orderTotals;
Once you have fixed your mappings and the CLI validator confirms it (orm:validate-schema), that collection will be correctly filtered

How to do Doctrine2 calculated fields ordering

I've got the following doctrine2 query working nicely, it retrieves all 'markers' within some geographical radius.
$qb->select('marker')
->from('SndSpecialistLocator\Entity\Marker', 'marker')
->where('DISTANCE(marker.location, POINT_STR(:point)) < :distance')
->setParameter('point', $point)
->setParameter('distance', $radius);
Now I want to sort them by distance.
$qb->select('marker (DISTANCE(marker.location, POINT_STR(:point))) AS distance')
->from('SndSpecialistLocator\Entity\Marker', 'marker')
->where('DISTANCE(marker.location, POINT_STR(:point)) < :distance')
->setParameter('point', $point)
->orderBy('distance', 'DESC')
->setParameter('distance', $radius);
But unfortunately this does not work, I am wondering is this possible as distance is not a real property of my entity, but a calculated one?
What is the trick here?
Alternatively, try using HIDDEN in your call to select() :
$qb->select('m as marker, (DISTANCE(m.location, POINT_STR(:point))) as HIDDEN distance')
// note HIDDEN added here --->------->------->------->------->------->---^
->from('SndSpecialistLocator\Entity\Marker', 'm')
->where('DISTANCE(m.location, POINT_STR(:point)) < :distance')
->setParameter('point', $point)
->orderBy('distance', 'ASC')
->setParameter('distance', $radius)
->setMaxResults(5);
$query = $qb->getQuery();
$result = $query->execute();
Adding HIDDEN to the SELECT clause hides it from the results but allows it to be used in the orderby clause. Your $result should then contain the objects you want without having to do the extra array_walk.
Unfortunately, ordering by aliases is not possible.
What you can do* instead is to manually repeat the function in your orderBy statement:
$qb->select('marker (DISTANCE(marker.location, POINT_STR(:point))) AS distance')
->from('SndSpecialistLocator\Entity\Marker', 'marker')
->where('DISTANCE(marker.location, POINT_STR(:point)) < :distance')
->setParameter('point', $point)
->orderBy('DISTANCE(marker.location, POINT_STR(:point))', 'DESC')
->setParameter('distance', $radius);
*We will probably all end up in dev-hell for stepping off the holy path of DRY
Found the solution.
$qb->select('m as marker, (DISTANCE(m.location, POINT_STR(:point))) as distance')
->from('SndSpecialistLocator\Entity\Marker', 'm')
->where('DISTANCE(m.location, POINT_STR(:point)) < :distance')
->setParameter('point', $point)
->orderBy('distance', 'ASC')
->setParameter('distance', $radius)
->setMaxResults(5);
$query = $qb->getQuery();
$result = $query->execute();
/**
* Convert the resulting associative array into one containing only the entity objects
*
* array (size=5)
* 0 =>
* array (size=2)
* 'marker' =>
* object(SndSpecialistLocator\Entity\Marker)[647]
* protected 'id' => int 43
* protected 'title' => string 'c5d07acdd47efbe38a6d0bf4ec64f333' (length=32)
* private 'location' =>
* object(SndSpecialistLocator\Orm\Point)[636]
* private 'lat' => float 52.2897
* private 'lng' => float 4.84268
* 'distance' => string '0.0760756823433058' (length=18)
* 1 => ...
*
* array (size=5)
* 0 =>
* object(SndSpecialistLocator\Entity\Marker)[647]
* protected 'id' => int 43
* protected 'title' => string 'c5d07acdd47efbe38a6d0bf4ec64f333' (length=32)
* private 'location' =>
* object(SndSpecialistLocator\Orm\Point)[636]
* private 'lat' => float 52.2897
* private 'lng' => float 4.84268
* 1 => ...
*
*/
array_walk($result, function (&$item, $key)
{
$item = $item['marker'];
});