How do i use subqueries in a left join as a table - mysql

Cakephp informs me that it cannot find the table or does not recognize the alias what am i supposed to use?
Hello i am new to cake php ORM can any one tell me how to preform a left join on a subquery I'm really interested to know how to use in working the join
Here is sub query and left join so far please ignore any syntax error
$scl = TableRegistry::get('School');
$subquery = $scl->find();
$subquery->select([
'UID',
'SID',
'Total' => $subquery->func()->sum('numberOfStudents')
])->group(['UID, SID']);
$q->select([
'TeacherID',
'ClassID',
'StudentTotal' => 'sq.Total'
])->join([
'table' => $subquery,
'alias' => 'sq',
'type' => 'LEFT',
'conditions' => ['sq.UID = TeacherID', 'sq.SID = ClassID']
]);
here is the error:
[PDOException] SQLSTATE[42S22]: Column not found: 1054 Unknown column 'sq.UID' in 'on clause'

The problem is that ORM based queries automatically alias the selected fields, so you don't get
SELECT UID, SID ...
but
SELECT UID as School__UID, SID as School__SID
hence referring to sq.UID will fail, as no such field name was selected.
To avoid this problem you can either use an alias that matches the original field name:
->select([
'UID' => 'UID',
'SID' => 'SID',
// ...
])
use the lower level database query that doesn't automatically create aliases:
$subquery = $scl
->getConnection() // connection() in older CakePHP versions
->newQuery()
->from($scl->getTable()); // table() in older CakePHP versions
or refer to the aliased fields in the main query:
'conditions' => [
'sq.' . $scl->getAlias() . '__UID = TeacherID', // alias() in older CakePHP versions
'sq.' . $scl->getAlias() . '__SID = ClassID',
]

Related

Using MySql DATE function in CakePHP 3 query where clause

Objective: Trying to get all posts on a certain date from the db table(say, I've datetime in the format '2019-03-07 12:30:00' then I would like to get all posts from this date '2019-03-07').
As I need posts from this date, I'm converting the given datetime to i18nFormat date format. As below ::
$userSelectedDate = $selectedDate->i18nFormat('yyyy-MM-dd');
then, On the where clause, I'm using mySql DATE function on table field and it returns me expected result. code as below:
$conn = connectionManager::get('default');
$conn->begin();
$stmt = $conn->prepare(
"SELECT * FROM `blogs`
where DATE(`PUBLISHED_DATE`) = '$userSelectedDate'"
);
$stmt->execute();
$conn->commit();
This works fine. But I would like to convert it to Cakephp 3 way as below.
$query = $this
->find()
->where([
DATE($this->aliasField('PUBLISHED_DATE')) => $userSelectedDate
])
;
This obviously throws an error as below.
Error: [PDOException] SQLSTATE[42S22]: Column not found: 1054 Unknown column
How to use mysql DATE function in Cakephp 3 query? I've checked other related answers and I couldn't find a way.
Managed to find the answer.
$query = $this->find("all", [
'conditions' => [
'DATE(PUBLISHED_DATE)' => $selectedDate->i18nFormat('yyyy-MM-dd')
]
]
);

Not equal condition with findAll()

I am trying to pull record from a table using the following code
$userId = Yii::$app->user->id;
$lists = PromoLists::findAll(['user_id' => $userId, 'list_type' => 'custom']);
which outputs a query like below
select * from promo_lists where user_id ='$userId' and list_type='custom'
But i am unable to find any thing in the documentation that would help me achieve it with the following condition.
select * from promo_lists where user_id ='$userId' and list_type='custom' and status!='deleted'
as the status is an ENUM field and there are 4 different status
'active','pending','rejected','deleted'
currently i used the following approach
PromoLists::findAll(['user_id' => $userId, 'list_type' => 'custom', 'status'=>['active','pending','rejected']]);
which outputsthe following query
select * from promo_lists where user_id ='$userId' and list_type='custom' and status in ('active','pending','rejected')
which somehow achieves the same thing but this query would need to be edited every time when there is a new status type added to the table column status.
i know i can do this by using PromoLists::find()->where()->andWhere()->all()
but how to check with != / <> operator using findAll().
Simply like this:
PromoLists::find()->where(['and',
[
'user_id' => $userId,
'list_type' => 'custom',
],
['<>', 'status', 'deleted'],
])->all();
Using operator format in condition
http://www.yiiframework.com/doc-2.0/guide-db-query-builder.html#operator-format
PromoLists::find()
->andWhere([
'user_id' => $userId,
'list_type' => 'custom',
['!=', 'status', 'deleted']
])
->all();

CakePHP 3.x - SUM() on ManyToMany

I am getting stuck on using SQL functions queries made in CakePHP 3 in combinations with associations.
The situation is as follows: I have three tables, a 'products' table, an 'orders' table and a join table called 'orders_products'.
In the index of OrdersController I would like to add the total price (= sum of relevant product prices) to the table of orders. In SQL this exactly can be done with the following query:
SELECT orders.id, SUM(products.price)
FROM orders
LEFT JOIN orders_products
ON orders.id = orders_products.order_id
LEFT JOIN products
ON orders_products.product_id = products.id
GROUP BY orders.id;
I figured to following controller code should do the trick:
$orders = $this->Orders->find('all')->contain(['Products']);
$orders
->select(['total_price' => $orders->func()->sum('Products.price')])
->group('Orders.id');
However, when the query object is executed, I get an error:
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column
'Products.price' in 'field list'
...Even though the association between orders and products is defined.
Calling only $orders = $this->Orders->find('all')->contain(['Products'])->all(); does return an array of orders with each order a number of products, the model has to be set up correctly. Any ideas what might be wrong? Thanks in advance!
From OrdersTable:
$this->belongsToMany('Products', [
'foreignKey' => 'order_id',
'targetForeignKey' => 'product_id',
'joinTable' => 'orders_products'
]);
And from ProductsTable:
$this->belongsToMany('Orders', [
'foreignKey' => 'product_id',
'targetForeignKey' => 'order_id',
'joinTable' => 'orders_products'
]);
One way to do it:
$orders = $this->Orders->find()
->select([
'order_id' =>'orders.id',
'price_sum' => 'SUM(products.price)'
])
->leftJoin('orders_products', 'orders.id = orders_products.order_id'),
->leftJoin('products', 'orders_products.product_id = products.id')
->group('orders.id');

hasMany relation without table having id column

I am using CakePHP 2.5. I am having following table
CompanyMaster:
company_master_id [PK]
Name and other columns
CompanySignatoryDetails: (has many owners for single company)
company_signatory_details_id [PK]
company_master_id [FK]
Name and other columns
Now, I want to get company details with all owners of that company. Here is what I have tried.
$this->CompanyMaster->bindModel(
array(
'hasMany' => array(
'CompanySignatoryDetails' => array(
'className' => 'CompanySignatoryDetails',
'foreignKey' => false,
'conditions' => array(
'CompanySignatoryDetails.company_master_id = CompanyMaster.company_master_id'
),
),
)
)
);
$this->CompanyMaster->recursive = 2;
$company = $this->CompanyMaster->find('first', array(
'fields' => array('CompanyMaster.*'),
'conditions' => $conditions, //company id in condition
));
I am getting following error:
Database Error
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'CompanyMaster.id' in 'field list'
SQL Query:
SELECT `CompanyMaster`.*, `CompanyMaster`.`id` FROM `crawler_output`.`company_master` AS `CompanyMaster` WHERE `CompanyMaster`.`company_master_id` = 1 LIMIT 1
Please let me know how can I bind model without id as column name.
CakePHP will produce a separate query when dealing with hasMany relationships, and therefore you won't be able to reference a field from another table. Only belongsTo and hasOne relationships produce a JOIN.
However, you don't need to add conditions to the relationship. The following should just work fine:
$this->CompanyMaster->bindModel(array(
'hasMany' => array(
'CompanySignatoryDetails' => array(
'className' => 'CompanySignatoryDetails',
'foreignKey' => 'company_master_id',
),
)
));
Don't forget to define your primary keys for CompanyMaster:
class CompanyMaster extends AppModel
{
public $primaryKey = 'company_master_id';
}
and for CompanySignatoryDetails:
class CompanySignatoryDetails extends AppModel
{
public $primaryKey = 'company_signatory_details_id';
}
Well, for instance, let your query looks like this:
select CompanyMaster.*,CompanySignatoryDetails.* from
CompanyMaster as cm inner join CompanySignatoryDetails as cd on
cm.company_master_id=cd.company_master_id
order by cm.company_master_id;
You will get all fields from two tables, ordered by company_master_id field. You may reduce number of fields, displayed by this query, by explicitly designate them like this:
select cm.company_master_id, cd.name from....
HNY!(Happy New Year!!)

help with SQL query in Yii

Given the following diagram:
With the code below I have the Donations grouped for each organization now I am trying to calculate the total amount a given member has donated to a given organization.
Something like:
With this code it correctly groups that organizations as I need but the problem I have here is that for the 'Amount Donated to Organization' column all values equal the total of the Organization with the highest Id. Therefore all rows in that column are showing $90
Yii Code:
// member view
<?php
$dataProvider=new CActiveDataProvider(Donation::model(), array(
'criteria'=>array(
'with' => array(
'member' => array(
'on'=>'member.MemberId='.$model->MemberId,
'group' => 't.MemberId, t.OrganizationId',
'joinType'=>'INNER JOIN',
),
),
'together'=> true,
),
));
$this->widget('zii.widgets.grid.CGridView', array(
'dataProvider'=>$dataProvider,
'columns' => array(
array(
'name'=>'OrganizationId',
'value' => '$data->OrganizationId',
),
array(
'name'=>'Amount',
'value' => '$data->memberOrgBalance;',
),
),
));
?>
// member model
'memberOrgBalance' => array(self::STAT, 'Donation', 'MemberId',
'select'=>'MemberId, OrganizationId, SUM(Amount)',
'group' => 'OrganizationId'),
// donation model
'member' => array(self::BELONGS_TO, 'Member', 'MemberId'),
EDIT: See also response to LDG
Using the advice from LDG I tried adding 'having' to my dataprovider, when that did not seem to affect the query I tried to add it to the relation memberOrgBalance where I am trying to pull the data. This seems to affect the query but it is still not right. I switched to:
'memberOrgBalance' => array(self::STAT, 'Donation', 'MemberId',
'select'=>'MemberId, OrganizationId, SUM(Amount)',
'group' => 'OrganizationId',
'having'=> 'MemberId=member.MemberId',
),
which gives this error:
CDbCommand failed to execute the SQL statement: SQLSTATE[42S22]:
Column not found: 1054 Unknown column 'member.MemberId' in 'having clause'.
The SQL statement executed was: SELECT `MemberId ` AS `c`, MemberId, OrganizationId,
SUM(Amount) AS `s` FROM `donation` `t` WHERE (`t`.`MemberId `='2')
GROUP BY `MemberId `, OrganizationId HAVING (MemberId=member.MemberId)
This makes no sense since from the donation table I have the relation defined as originally posted above. The query seems to be going the direction needed to get the SUM per organization as I want though. Any other ideas?
If I understand what you are trying to it would seem like you need to add a "having" attribute, something like
'on'=>'member.MemberId = t.MemberId',
'group' => 't.MemberId, t.OrganizationId',
'having'=> 't.MemberId=' . $model->MemberId
This is the SQL query needed..
select donation_org_id , sum(donation_amount) as donated_amount, count(d.donation_id) as members_count
from donations d
group by d.donation_org_id
Ok after running around in circles with this someone was able to push me over the top to a solution on Yii forums.
The end result is
$criteria->condition='member.MemberId="'.$model->MemberId.'"';
$criteria->with='member';
$criteria->select='MemberId,OrganizationId,sum(Amount) as Amount';
$criteria->group='t.MemberId,OrganizationId';
$dataProvider=new CActiveDataProvider(Donations::model(),
array(
'criteria'=>$criteria,
Thanks ldg for the help with this.