LEFT JOIN Association returns unknown column - php

I am using PHPActiveRecord and CodeIgniter. I am trying to create a blog CMS and a blog has many categories and its referring to another table called blogrefs. So basically
blogs => blogrefs => blogcats
blogrefs is referring with blogs through blogrefs.blog_id
blogrefs is referring with blogcats through blogrefs.blogcat_id
function get_blogs($params = array()){
$CI =& get_instance();
$joins = 'LEFT JOIN blogrefs ON blogs.id = blogrefs.blog_id';
$joins .= 'LEFT JOIN blogcats ON blogrefs.blogcat_id = blogcats.id';
$category = (isset($params['category']))?$params['category']:'';
$keyword = (isset($params['keyword']))?$params['keyword']:'';
$status = (!isset($params['status']) || $params['status'] == '')?'':$params['status'];
$order_by = (isset($params['order_by']))?$params['order_by']:'created_on';
$direction = (isset($params['direction']))?$params['direction']:'DESC';
$limit = (isset($params['limit']))?$params['limit']:'';
$offset = (isset($params['offset']))?$params['offset']:'';
$st = '';
if($status == ''){
$st = '!';
}
if(!empty($category))
{
$conditions = array(
"((blogcats.id = ?) OR (blogcats.parent_id = ?))
AND blogs.status $st= ?
AND (blogs.title LIKE '%$keyword%' OR blogs.content LIKE '%$keyword%')",$category,$category,$status
);
}else{
$conditions = array(
"blogs.status $st= ?
AND (blogs.title LIKE '%$keyword%' OR blogs.content LIKE '%$keyword%')",$status
);
}
$result = Blog::find('all',array(
'include' => array('blogcat','blogref','user'),
'joins' => $joins,
'conditions' => $conditions,
'limit' => $limit,
'offset' => $offset,
'order' => "$order_by $direction",
'group' => 'blogs.id'
));
$count = Blog::find('all',array(
'conditions' => $conditions
));
$object = new stdClass;
$object->posts = $result;
$object->total = count($count);
return $object;
}
Everything works not only if i use
((blogcats.id = ?) OR (blogcats.parent_id = ?))
To get blogs from specific category. Any answers will be very much appreciated.

I don't find this very clear. Why not use a model and CI's Active Record Class? (http://ellislab.com/codeigniter/user-guide/database/active_record.html), like
$this->db->where('blogrefs.blogcat_id', $catid);
$this->db->join('blogrefs', 'blogs.id = blogrefs.blog_id');
...

Your error is a missing space at the end of where you define the first part of your join.
$joins = 'LEFT JOIN blogrefs ON blogs.id = blogrefs.blog_id';
$joins .= 'LEFT JOIN blogcats ON blogrefs.blogcat_id = blogcats.id';
evaluates to:
LEFT JOIN blogrefs ON blogs.id = blogrefs.blog_idLEFT JOIN blogcats ON blogrefs.blogcat_id = blogcats.id
You'll notice that the first join is screwed up by looking for a column named 'blog_idLEFT' and that the second JOIN will now be an inner join, not an outer left join.
I'd recommend either going with davedriesmans's suggestion, or finding someway to output the final mySQL queries you are generating. (Or both)

Related

Doctrine - How do you use a subquery column (in the SELECT clause and having condition)

Hello i am trying to rewrite ZF query builder into doctrine ORM.
Here IS mine old Zend framework query.
$dependent = new Zend_Db_Expr('if(br.billing_rate = \'dependent\',(SELECT COUNT(*) FROM childcare_billing_rule_price AS p WHERE p.id < brp.id AND p.childcare_billing_rule_id = brp.childcare_billing_rule_id),0)');
$select = $this
->select()->setIntegrityCheck(false)
->from(array('brg' => 'childcare_billing_rule_group'), array())
->joinInner(array('br' => 'childcare_billing_rule'), 'br.id = brg.billing_rule_id', array('*', 'dependent' => $dependent))
->join(array('brp' => 'childcare_billing_rule_price'), 'br.id = brp.childcare_billing_rule_id', array('age_from', 'age_to', 'default_rate' => 'rate'))
->where('is_default != 1')
->where('br.billing_type in (\'' . implode('\',\'', array(self::BILLING_TYPE_PER_DAY, self::BILLING_TYPE_PER_HOUR, self::BILLING_TYPE_PER_UNLIMITED, self::BILLING_TYPE_PER_RESERVATION)) . '\')')
->where('brg.group_id IN (?)', $groupId)
->group('brp.id')
->having('dependent <= ?', $dependentLevel)
->order('dependent desc')
->order('brp.rate');
And here it is SQL row value
SELECT `br`.*,
if (br.billing_rate = 'dependent',
(SELECT COUNT(*) FROM childcare_billing_rule_price AS p WHERE p.id < brp.id AND p.childcare_billing_rule_id = brp.childcare_billing_rule_id),
0) AS `dependent`,
`brp`.`age_from`,
`brp`.`age_to`,
`brp`.`rate` AS `default_rate`
FROM `childcare_billing_rule_group` AS `brg`
INNER JOIN `childcare_billing_rule` AS `br` ON br.id = brg.billing_rule_id
INNER JOIN `childcare_billing_rule_price` AS `brp` ON br.id = brp.childcare_billing_rule_id
WHERE (is_default != 1)
AND (br.billing_type in ('Per Day','Per Hour','Unlimited','Per Reservation'))
AND (brg.group_id IN (103))
AND ((brp.age_from = 0 AND brp.age_to = 0) || 3.1666666666667
BETWEEN brp.age_from AND brp.age_to)
GROUP BY `brp`.`id` HAVING (dependent <= '0')
ORDER BY `dependentw` desc,
`brp`.`rate` ASC
I am not sure how to implement this sub query
new Zend_Db_Expr('if(br.billing_rate = \'dependent\',(SELECT COUNT(*) FROM childcare_billing_rule_price AS p WHERE p.id < brp.id AND p.childcare_billing_rule_id = brp.childcare_billing_rule_id),0)');
My code so far
$subQuery = $subQueryBuilder
->select('if (br.billing_rate = 'dependent',
(SELECT COUNT(*) FROM childcare_billing_rule_price AS p WHERE p.id < brp.id AND p.childcare_billing_rule_id = brp.childcare_billing_rule_id)')
->getSQL();
$queryBuilder->select($alias)
->addSelect($subQuery)
->innerJoin(ChildcareBillingRule::class, 'br', Join::WITH, 'br.id = childcare_billing_rule_group.childcareBillingRule')
->innerJoin(ChildcareBillingRulePrice::class, 'brp', Join::WITH, 'br.id = brp.childcareBillingRule')
->where("br.billingType in (:billingTypes)")
->andWhere( 'br.isDefault != :isDefault')
->andWhere( 'childcare_billing_rule_group.groupId in (:groupId)')
->setParameter('groupId', [103])
->setParameter('isDefault', 1)
->setParameter('billingTypes', ['Per Hour', 'Per Reservation', 'Per Day', 'Unlimited'])
->having('dependent <= ?', $dependentLevel')
->groupBy('brp.id')
->orderBy('brp.rate') ;
->getQuery()->getSingleResult();
so i am trying to add sub query with addSelect but not sure is this the right way? Any Help or idea is appreciated? Tnx
in short instead of ->getSQL() you should use ->getDQL().
Yours code will be like:
$dql = $this->createQueryBuilder()
->select('count(id)')
->from({Class::FQDN}, {ALIAS})
->getDQL();
$result = $this->createQueryBuilder()
->select({SECOND_ALIAS})
->from({SecondClass::FQDN}, {SECOND_ALIAS})
->where((new \Expr())->gt('{SECOND_ALIAS}.cnt', $dql))
->getQuery()
->getResult();

Subquery With Yii Active Record

Is it possible to write sub queries with Yii active record?
I need to add a subquery in the FROM clause. Below is the SQL with the subquery but I want to specifically write it using Active Record.
In SQL, Policy has a many to many relationship with policy_vehicles.
SQL Query:
select * from (SELECT Name , GROUP_CONCAT(PV.VehicleId SEPARATOR ',') as VehicleIds
FROM `policies` `t`
LEFT JOIN policy_vehicles PV USING(PolicyId)
WHERE (Type IN ('C', 'E')) AND (Status='Active')
GROUP BY t.PolicyId
) k where VehicleIds like '%1%' AND VehicleIds like '%2%'
This is how I want to use it in the Controller class:
$model = Policy::model();
$model->Status = array('Active');
$model->VehicleIds = array(1,2);
$model->Type = array('C', 'E');
$datas = $model->search()->getData();
print_r($datas);
Search function in the model:
public function search() {
$joins = array();
$criteria = new CDbCriteria;
$criteria->compare('Type', $this->Type);
$criteria->compare('Status', $this->Status);
array_push($joins, 'LEFT JOIN policy_vehicles PV USING(PolicyId)');
$criteria->join = implode(' ', $joins);
return new CActiveDataProvider($this, array(
'criteria' => $criteria,
));
}

Zend Framework 2: how to get columns from joined tables?

I am using Zend Framework 2.4 with TableGateway and need to execute the following query:
SELECT products.title, student.firstname, orders.order_id,
orders.profile_id, orders.status
FROM student
INNER JOIN
(products
INNER JOIN orders ON products.product_id = orders.product_id)
ON student.student_id = orders.student_id
WHERE (((orders.profile_id)='.$id.'));
Here is what I am using:
$select = $this->tableGateway->getSql()->select();
$select->columns(array('*'))
->join('products', 'products.product_id = orders.product_id', array('title'))
->join('student', 'student.student_id = orders.student_id', array('first_name'))
->where(array('orders.profile_id' => $id));
$items = $this->tableGateway->selectWith($select);
return $items;
The problem is that $items does not contain the columns title or first_name. It only contains the columns from the orders table.
What do I need to do to get $items to contain the columns from the joined tables?
try to use an associative array
$select = $this->tableGateway->getSql()->select();
$select->columns(array('*'))
->join('products', 'products.product_id = orders.product_id', array('title' => 'product_title'))
->join('student', 'student.student_id = orders.student_id', array('first_name' => 'student_first_name'))
->where(array('orders.profile_id' => $id));
$items = $this->tableGateway->selectWith($select);
return $items;

How can I do a join with last entries in CakePHP?

So, I'm trying to do a query to get the last entries of each user using CakePHP relationship. In my Map and User model I have:
class Map (and User) extends AppModel {
public $hasMany = 'Geolog';
}
And I'm trying to doing this query to catch the last entry of each user. dg_maps and dg_users have a many to many relation.
SELECT
dg_users.id,
dg_users.email,
dg_maps.id,
dg_maps.latitude,
dg_maps.longitude,
dg_maps.timestamp
FROM
(SELECT
*
FROM
dg_maps
ORDER BY dg_maps.timestamp DESC) dg_maps
JOIN
dg_geologs ON dg_geologs.map_id = dg_maps.id
JOIN
dg_users ON dg_users.id = dg_geologs.user_id
GROUP BY dg_geologs.user_id;
What I'm doing now is:
$this->Map->query('SELECT dg_users.id, dg_users.email, dg_maps.id, dg_maps.latitude, dg_maps.longitude, dg_maps.timestamp FROM (SELECT * FROM dg_maps ORDER BY dg_maps.timestamp DESC) dg_maps JOIN dg_geologs ON dg_geologs.map_id = dg_maps.id JOIN dg_users ON dg_users.id = dg_geologs.user_id GROUP BY dg_geologs.user_id');
But, I know that a better method then this, using Cake methods. So, how can I do this?
Thank you all!
I used this for my project it is very simple
Easy way to generate CakePHP HABTM joins for use in pagination.
So put this in your model (note for function arguments - there is error in function from link )
public function generateHabtmJoin ($joinModel, $joinType = 'INNER') {
// If the relation does not exist, return an empty array.
if (!isset($this->hasAndBelongsToMany[$joinModel])) {
return array();
}
// Init joins, and get HABTM relation.
$joins = array();
$assoc = $this->hasAndBelongsToMany[$joinModel];
// Add the join table.
$bind = "{$assoc['with']}.{$assoc['foreignKey']} = {$this->alias}.{$this->primaryKey}";
$joins[] = array(
'table' => $assoc['joinTable'],
'alias' => $assoc['with'],
'type' => $joinType,
'foreignKey' => false,
'conditions' => array($bind),
);
// Add the next table.
$bind = "{$joinModel}.{$this->{$joinModel}->primaryKey} = {$assoc['with']}.{$assoc['associationForeignKey']}";
$joins[] = array(
'table' => $this->{$joinModel}->table,
'alias' => $joinModel,
'type' => $joinType,
'foreignKey' => false,
'conditions' => array($bind),
);
return $joins;
}
and you can call this function from your controller
You have example in link

How can I turn ZF ->joinLeft()s into a Subquery?

How would i convert below query to sub query?
I don't want to use JOINS I want to do same through sub query. i-e I need three subqueries out of the three joins. How it would be possible for below query ?
protected $_name = 'sale_package_features';
public function getAllSalePackageFeatures(){
$sql = $this->select()->setIntegrityCheck(false)
->from(array('spf' => $this->_name))
->joinLeft(array('sd' => 'sale_devices'),'sd.sale_device_id = spf.sale_device_id',array('sd.sale_device_name AS deviceName'))
->joinLeft(array('sp' => 'sale_packages'),'sp.sale_package_id = spf.sale_package_id',array('sp.sale_package_name AS packageName'))
->joinLeft(array('sf' => 'sale_features'),'sf.sale_feature_id = spf.sale_feature_id',array('sf.sale_feature_name AS featureName'))
->where('sf.parent_id != ?',0)
->order('spf.sale_package_feature_id ASC');
return $sql->query()->fetchAll();
}
Edited :
SELECT
`spf`.*, `sd`.`sale_device_name` AS `deviceName`,
`sp`.`sale_package_name` AS `packageName`,
`sf`.`sale_feature_name` AS `featureName`
FROM `sale_package_features` AS `spf`
LEFT JOIN `sale_devices` AS `sd`
ON sd.sale_device_id = spf.sale_device_id
LEFT JOIN `sale_packages` AS `sp`
ON sp.sale_package_id = spf.sale_package_id
LEFT JOIN `sale_features` AS `sf`
ON sf.sale_feature_id = spf.sale_feature_id
WHERE (sf.parent_id != 0)
ORDER BY `spf`.`sale_package_feature_id` ASC
You can use
$dbAdapter = Zend_Db_Table::getDefaultAdapter();
$subSelect = $dbAdapter
->select()
->from( 'tablename',
array(
'col1_alias' => 'column1_name',
'col2_alias' => 'column2_name',
))
->where('somefield = ?', $value_to_be_quoted);
$select = $dbAdapter
->select()
->from(array('sub_alias' => $subSelect ))
->where('otherfield = ?', $other_value_to_be_quoted);
$sql = $select->assemble();
$sql will contain
SELECT
sub_alias . *
FROM
(SELECT
tablename.column1_name AS col1_alias,
tablename.column2_name AS col2_alias
FROM
tablename
WHERE
(somefield = 'quoted_value')) AS sub_alias
WHERE
(otherfield = 'quoted_other_value')
If you need to use SELECT .. WHERE x IN (subselect) than go with
$select->where( 'column IN (?)', new Zend_Db_Expr($subselect->assemble()) );

Categories