How order doctrine query for many-to-many relationship? - symfony1

i use doctrine 1.2 and here is my schema:
ProviderProduct:
columns:
provider_id:
type: integer
primary: true
product_id:
type: integer
primary: true
num:
type: integer
default: 0
unsigned: true
Provider:
columns:
name: {type: string(255), notnull: true, notblank: true, unique: true}
relations:
Products:
class: Product
local: provider_id
foreign: product_id
refClass: ProviderProduct
Product:
columns:
#a lot of columns
relations:
Providers:
class: Provider
local: product_id
foreign: provider_id
refClass: ProviderProduct
What i want is get product with max num (this is ammount) for specific provider.
I tried this query:
$query = Doctrine_Query::create()->select('p.*')
->from('Product p')
->innerJoin('p.Providers pr')
->where('pr.name = ?', 'WhiteStore')
->orderBy('ProviderProduct.num');
$query->fetchOne();
The result sql query:
SELECT p.* FROM product p
INNER JOIN provider_product p3 ON (p.id = p3.product_id)
INNER JOIN provider p2 ON p2.id = p3.provider_id, provider_product p4
WHERE (p2.name = ?) ORDER BY p4.num
As you can see it doesnt order by num field. So, what is the right dql for my task ?

How order doctrine query for many-to-many relationship?
You can use following construction:
$query = Doctrine_Query::create()->select('p.*')
->from('Product p')
->innerJoin('p.Providers pr')
->where('pr.name = ?', 'WhiteStore')
->orderBy('p.Providers.ProviderProduct.num');
The not native behavior of Doctrine 1 is creating an invisible alias for many-to-many relation. You can use this alias for building right order.

I think you need to relate refClass too:
ProviderProduct:
columns:
provider_id:
type: integer
primary: true
product_id:
type: integer
primary: true
num:
type: integer
default: 0
unsigned: true
relations:
Provider: { foreignAlias: ProviderProduct }
Product: { foreignAlias: ProviderProduct }
And then relate through reffClass to get the number:
$query = Doctrine_Query::create()->select('p.*')
->from('Product p')
->innerJoin('p.ProviderProduct pp')
->innerJoin('pp.Provider pr')
->where('pr.name = ?', 'WhiteStore')
->orderBy('pp.num DESC') // to get de max first
->groupBy('p.id') // if any duplicates
->limit(1);
$product = $query->fetchOne();

Related

Manually save a many to many relation in Doctrine

I have just created a new project with a many-to-many relation (User-Group) and the code below.
As you can guess in the code I'm trying to assign manually groups to a user, but it doesn't assign it anything...any idea?
User:
columns:
id:
type: integer(4)
autoincrement: true
primary: true
username:
type: string(255)
password:
type: string(255)
attributes:
export: all
validate: true
Group:
tableName: group_table
columns:
id:
type: integer(4)
autoincrement: true
primary: true
name:
type: string(255)
relations:
Users:
foreignAlias: Groups
class: User
refClass: GroupUser
GroupUser:
columns:
group_id:
type: integer(4)
primary: true
user_id:
type: integer(4)
primary: true
relations:
Group:
foreignAlias: GroupUsers
User:
foreignAlias: GroupUsers
The code is this:
public function executeIndex(sfWebRequest $request)
{
$user = Doctrine_Core::getTable('User')->find(1);
$groups = Doctrine_Core::getTable('Group')->findAll();
$user->setGroups($groups);
$user->save();
$this->forward('default', 'module');
}
Try defining the relation in the User entity too (and don't using foreingAlias in Groups). Also use the local and foreign fields.
So, schema.yml should look like:
User:
...
relations:
Groups:
class: Group
refClass: GroupUser
local: user_id
foreign: group_id
Group:
...
relations:
Users:
class: User
refClass: GroupUser
local: group_id
foreign: user_id
If that doesn't work, I think you can set the groups with the Doctrine "link" method.
See Record.php (probably in lib/vendor/symfony/lib/plugins/sfDoctrinePlugin/lib/vendor/doctrine/Doctrine folder)
Example:
$groups_ids = array();
foreach($groups as $group) $groups_ids[] = $group->getId();
$user->link('Groups',$groups_ids);
Also, if you are trying only to add instances to that relation (and you hate the person who will fix your code in the future) you can create N-GroupUsers
Example:
foreach($groups as $group) {
$ug = new GroupUser();
$ug->setUserId($user->getId());
$ug->setGroupId($user->getId());
$ug->save();
}
Please, use the first option and let me know if any of this works for you!

Symfony sorted model with having request

I have two models : Operation and OperationHistory
Operation:
columns:
startdate: { type: timestamp, notnull: true }
enddate: { type: timestamp, notnull: true }
year: { type: integer, notnull: true }
abscomment: { type: string(500) }
person_id: { type: integer, notnull: true }
operationtype_id: { type: integer, notnull: true }
relations:
Person: { onDelete: CASCADE, local: person_id, foreign: id, foreignAlias: Operations}
OperationType: { onDelete: CASCADE, local: absencetype_id, foreign: id, foreignAlias: Operations }
OperationHistory:
columns:
oh_comment: { type: string(500), notnull: true }
oh_date: { type: timestamp, notnull: true }
status_id: { type: integer, notnull: true }
operation_id: { type: integer, notnull: true }
updater: { type: integer, notnull: true }
indexes:
date:
fields:
ohdate:
sorting: DESC
relations:
Operation: { onDelete: CASCADE, local: operation_id, foreign: id, foreignAlias: OperationsHistory }
Person: { onDelete: CASCADE, local: updater, foreign: id, foreignAlias: OperationsHistory }
OperationStatus: { onDelete: CASCADE, local: status_id, foreign: id, foreignAlias: OperationsHistory }
In order to get all Operation by Person, I use an index on OperationHistory. So if I need all the person's operation with a specific status:
public function getOperations($person, $status){
$q = $this->createQuery('o')
->leftJoin('o.OperationsHistory oh')
->leftJoin('p.Person p')
->groupBy('ah.absence_id')
->having('ah.status_id = ?', $status);
return $q->execute();
}
Thanks to the index on ohdate, I assume with the groupBy, I only get the last OperationHistory about a specific Operation. Without the having clause, It's good, but I only want Operations with a specific Status. But if I set this having clause, I get nothing at all.
In fact, I need to translate this sql request :
SELECT *
FROM operation o
LEFT JOIN (
SELECT *
FROM operation_history
ORDER BY ohdate DESC
) oh ON o.id = oh.absence_id
LEFT JOIN person p ON p.id = o.person_id
WHERE p.id = 1
GROUP BY oh.operation_id
HAVING oh.status_id = 1
sorry for my bad english and I hope these informations will be usefull to help me.
Thank u !
Its very difficult to understand what you are trying to do just based on your questions above - can you include some other information ?
You can use join and orderby methods of the Doctrine_Query class :
$query = Doctrine_Core::getTable('operation')->createQuery()
->innerjoin('operation_history ......')
->orderby('.....')
By using Doctrine_RawSql(), it works !
public function getAbsenceQueries($person, $status){
$q = new Doctrine_RawSql();
$q->select('{o.*}, {oh.*}')
->from('operation o LEFT JOIN (SELECT * from operation_history order by ohdate DESC) oh ON o.id=oh.operation_id LEFT JOIN person p ON p.id = o.person_id')
->where('mg.group_id = ?', $group)
->groupBy('ah.absence_id')
->having('ah.status_id = ?', $status)
->addComponent('o','Operation o')
->addComponent('oh','o.OperationsHistory oh')
->addComponent('p','o.Person p');
return $q->execute();
}

Symfony doctrine , unique records

I use symfony 1.4.11 with doctrine.This is one of my tables:
Subscriptions:
connection: doctrine
tableName: subscriptions
columns:
user_id: { type: integer(4), primary: true }
category_id: { type: integer(4), primary: true }
relations:
sfGuardUser: { onDelete: CASCADE, local: user_id, foreign: id }
Categories: { onDelete: CASCADE, local: category_id, foreign: category_id }
I need to get all user_id from this table.
I make :
public function getSubscriptionsUser()
{
$q = $this->createQuery('a')
->select ('a.user_id');
return $q-> execute();
}
But if the user is subscribed to several categories, its id will be repeated several times. Is it possible to extract only unique id of user? If user have id = 1 , and it is repeated 10 times,in result I will have only "1" , but no "1 1 1 1 1 1 1 1 1 1" :-)
Thank you!
This should work out for you:
$q = $this->createQuery('a')
->select ('distinct(a.user_id) as user_id');

How to use doctrine or symfony pager with different column selected from multiple table?

How can I implement pagination using Doctrine_Pager or sfDoctrinePager while my query selects multiple columns from two or more tables ?
Edit1:
Ok, now I figured out that it can be done how Nathan has described below! I got confused as I couldn't retrieve certain data from the query! Let me describe it below:
This is my pager query:
$pager = new sfDoctrinePager('sfGuardUser', '5');
$q = Doctrine_Query::create()
->select('u.id, u.username, p.org_name, g.name, l.status')
->from('sfGuardUser u')
->leftJoin('u.Profile p')
->leftJoin('u.Groups g')
->leftJoin('u.LicensedVendors l')
->where('g.name = \'client\'');
$pager->setQuery($q);
$pager->setPage($request->getParameter('page', 1));
$pager->init();
Now in my Template I can retrieve my sfGuardUser and Profile data like this:
foreach ($pager->getResults() as $data) {
echo $data->username ; //outputs 'username' from sfGuardUser table
echo '<br />' ;
echo $data->Profile->org_name ; //outputs 'Organization name' from sfGuardUserProfile table
}
I was wrongly trying to retrieve the profile data by $data->org_name and not $data->Profile->org_name! Now its working for this part correctly, but there is still an issue !
I am still unable to retrieve the Groups & LicensedVendors data using $data->Groups->name or $data->LicensedVendors->status ! It does not show any error or any value either! looks like it outputs an empty string. Shouldn't it get the value just like Profile data ?
But when I hydrate the query by setting:
$q->setHydrationMode(Doctrine_Core::HYDRATE_SCALAR);
I can retrieve all data through:
foreach ($pager->getResults() as $data) {
echo $data['u_username'];
echo $data['p_org_name'];
echo $data['g_name'];
echo $data['l_status'];
}
How to get those data without setting **Doctrine_Core::HYDRATE_SCALAR** ? Where I'm doing wrong for retrieving those Groups and LicensedVendors table data?
Here is the schema definition of the tables described above:
License:
actAs: [Timestampable]
tableName: licenses
columns:
id:
type: integer(4)
primary: true
notnull: true
autoincrement: true
status:
type: enum
values: ['approved','pending_admin','pending_client','pending_vendor','rejected']
default: 'pending'
client_id:
type: integer(8)
notnull: true
vendor_id:
type: integer(8)
notnull: true
product_desc:
type: clob(16777215)
supplier_name:
type: string(80)
other_desc:
type: string(50)
financial_statement:
type: clob
relations:
VendorUser:
class: sfGuardUser
local: client_id
foreign: id
foreignAlias: LicensedVendors
onDelete: cascade
foreignType: many
owningSide: true
ClientUser:
class: sfGuardUser
local: vendor_id
foreign: id
foreignAlias: LicensedClients
onDelete: cascade
foreignType: many
owningSide: true
sfGuardUser:
actAs: [Timestampable]
columns:
first_name: string(255)
last_name: string(255)
email_address:
type: string(255)
notnull: true
unique: true
username:
type: string(128)
notnull: true
unique: true
algorithm:
type: string(128)
default: sha1
notnull: true
salt: string(128)
password: string(128)
is_active:
type: boolean
default: 1
is_super_admin:
type: boolean
default: false
last_login:
type: timestamp
indexes:
is_active_idx:
fields: [is_active]
relations:
Groups:
class: sfGuardGroup
local: user_id
foreign: group_id
refClass: sfGuardUserGroup
foreignAlias: Users
sfGuardUserProfile:
actAs:
Timestampable: ~
columns:
user_id:
type: integer
notnull: true
email:
type: string(80)
notnull: true
unique: true
email_new:
type: string(80)
unique: true
firstname:
type: string(30)
lastname:
type: string(70)
org_name:
type: string(80)
notnull: true
relations:
User:
class: sfGuardUser
foreign: id
local: user_id
type: one
onDelete: cascade
foreignType: one
foreignAlias: Profile
sfGuardGroup:
actAs: [Timestampable]
columns:
name:
type: string(255)
unique: true
description: string(1000)
relations:
Users:
class: sfGuardUser
refClass: sfGuardUserGroup
local: group_id
foreign: user_id
foreignAlias: Groups
Edit2: I posted my new issues which I described in first edit as a separate question here !
I guess as long as your query gives back a Doctrine_Collection object, you can use it with a pager, can't you?
Yeah, what greg0ire said. This documentation is a bit old, but it shows what you'd need with Propel in the old days. Updating to Doctrine would be like,
public function executeList ()
{
$pager = new sfDoctrinePager('Comment', 2);
$q = Doctrine_Core::getTable('Comment')
->createQuery('c')
->where('c.author = ?', 'Steve')
->leftJoin('c.Article a')
->andWhere('a.content LIKE ?', '%enjoy%')
->orderBy('c.created_at ASC');
$pager->setQuery($q);
$pager->setPage($request->getParameter('page', 1));
$pager->init();
$this->pager = $pager;
}
This blog post, "Symfony doctrine pager for two tables" has a more extended/convoluted example. Oh, looks like that was the author's answer to his own SO question.

Many to many relation on the same table

I'm trying to setup a m2m relation between 'Gabarits'.
One gabarit can have many Gabarits ( called Options )
One gabarit can come from many Gabarits ( called OptionsFrom )
Here is my schema.yml:
Gabarit:
actAs: [Attachable]
columns:
libelle: { type: string, size: 255 }
description: { type: clob }
relations:
Options:
class: Gabarit
refClass: Gabarit2Gabarit
local: gabarit_id
foreign: fils_id
foreignAlias: OptionsFrom
Gabarit2Gabarit:
columns:
fils_id: { type: integer, primary: true }
gabarit_id: { type: integer, primary: true }
relations:
Gabarit:
class: Gabarit
local: gabarit_id
alias: Gabarit
foreignAlias: Gabarit2Gabarits
Fils:
class: Gabarit
local: fils_id
alias: Fils
foreignAlias: Gabarit2Gabarits
Generated code is:
$this->hasMany('Gabarit as Options', array(
'refClass' => 'Gabarit2Gabarit',
'local' => 'gabarit_id',
'foreign' => 'fils_id'));
$this->hasMany('Gabarit as OptionsFrom', array(
'refClass' => 'Gabarit2Gabarit',
'local' => 'fils_id',
'foreign' => 'gabarit_id'));
It seems good, according to http://www.doctrine-project.org/projects/orm/1.2/docs/manual/defining-models/en#relationships:join-table-associations:self-referencing-nest-relations:non-equal-nest-relations
Logically, it would give me this:
foreach($gabarit_>getOptions() as $option)
{
in_array($gabarit->getId(), $options->getOptionsFrom()->getPrimaryKeys()); // should be true but returns false !!
}
Generated SQL for getOptions() :
SELECT gabarit.id AS gabarit__id, gabarit.libelle AS gabarit__libelle, gabarit.description AS gabarit__description, gabarit2_gabarit.fils_id AS gabarit2_gabarit__fils_id, gabarit2_gabarit.gabarit_id AS gabarit2_gabarit__gabarit_id FROM gabarit INNER JOIN gabarit2_gabarit ON gabarit.id = gabarit2_gabarit.fils_id WHERE gabarit.id IN (SELECT fils_id FROM gabarit2_gabarit WHERE gabarit_id = '507') ORDER BY gabarit.id ASC
Generated SQL for getOptionsFrom() :
SELECT gabarit.id AS gabarit__id, gabarit.libelle AS gabarit__libelle, gabarit.description AS gabarit__description, gabarit2_gabarit.fils_id AS gabarit2_gabarit__fils_id, gabarit2_gabarit.gabarit_id AS gabarit2_gabarit__gabarit_id FROM gabarit INNER JOIN gabarit2_gabarit ON gabarit.id = gabarit2_gabarit.gabarit_id WHERE gabarit.id IN (SELECT gabarit_id FROM gabarit2_gabarit WHERE fils_id = '529') ORDER BY gabarit.id ASC
Do you have any idea why Gabarit::getOptionsFrom returns an empty collection ?
thanks in advance,
Florian.
It will most probably be the fact that you are using the same alias
foreignAlias: Gabarit2Gabarits
Your Gabarit2Gabarit should be like this:
Gabarit2Gabarit:
columns:
fils_id: { type: integer, primary: true }
gabarit_id: { type: integer, primary: true }
relations:
Gabarit:
class: Gabarit
local: gabarit_id
alias: Gabarit
foreignAlias: Gabarit2GabaritsGabarit
Fils:
class: Gabarit
local: fils_id
alias: Fils
foreignAlias: Gabarit2GabaritsFils
That might help?

Resources