I'm currently having an issue with pagination in Zend Framework 2.
This code
public function findAllByCriteria(CourseSearchInput $input) {
$concatDelimiter = self::CONCAT_DELIMITER;
$select = new Select();
$where = new Where();
$having = new Having();
$select->columns(array(
'id', 'title', 'description'
));
$select->from($this->tableGateway->getTable());
$select
->join('coursedata', 'courses.id = coursedata.id', array(
'relevance' => $this->buildRelevanceExpressionFromCriteria($input)
))
;
$having
->greaterThanOrEqualTo('relevance', self::RELEVANCE_MIN);
;
$select->where($where, Predicate::OP_AND);
$select->having($having);
$select->group(array('courses.id'));
$dbAdapter = $this->tableGateway->getAdapter();
// $dbAdapter->getDriver()->getConnection()->execute('SET sql_mode = "";');
$adapter = new \Zend\Paginator\Adapter\DbSelect($select, $dbAdapter);
$paginator = new \Zend\Paginator\Paginator($adapter);
return $paginator;
}
create this SQL:
SELECT
`courses`.`id` AS `id`,
`courses`.`title` AS `title`,
`courses`.`description` AS `description`,
MATCH (coursedata.title) AGAINST ('Salsa') * 5 + MATCH (coursedata.description) AGAINST ('Salsa') * 2 AS `relevance`
FROM `courses`
INNER JOIN `coursedata` ON `courses`.`id` = `coursedata`.`id`
GROUP BY `courses`.`id`
HAVING `relevance` >= '3'
It ueses the MySQL Extensions to GROUP BY and cannot be executed, if the sql_mode is set to ONLY_FULL_GROUP_BY. So, I tried to reset the sql_mode before the statement is executed (see the commented out line above: $dbAdapter->getDriver()->getConnection()->execute('SET sql_mode = "";');). But it didn't worked. So, how can I set the sql_mode in order to execute my non-standard SQL?
This may not be the answer to the question you are asking, but I can see you are going to have an issue with your query regardless when using Paginator.
The DbSelect Adapter for the Paginator doesn't like the aggregate function in there (Group By)
The Paginator will try and use your query to build it's own query to calculate the "count" for items in the collection. This is broken due to you using an aggregate in your query, any groups etc will break the adapter.
if you check the default implementation you will see:
/**
* Returns the total number of rows in the result set.
*
* #return integer
*/
public function count()
{
if ($this->rowCount !== null) {
return $this->rowCount;
}
$select = clone $this->select;
$select->reset(Select::COLUMNS);
$select->reset(Select::LIMIT);
$select->reset(Select::OFFSET);
// This won't work if you've got a Group By in your query
$select->columns(array('c' => new Expression('COUNT(1)')));
$statement = $this->sql->prepareStatementForSqlObject($select);
$result = $statement->execute();
$row = $result->current();
$this->rowCount = $row['c'];
return $this->rowCount;
}
this doesn't like when you are using Group BY and will give back incorrect results.
You can create your own adataper, and extend the existing DbSelect and override the count method when you are planning to use Group BY;
Off the top of my head something like this should work, but may not be the most efficient way of doing it
/**
* Returns the total number of rows in the result set.
*
* #return integer
*/
public function count()
{
if ($this->rowCount !== null) {
return $this->rowCount;
}
/**
* If the query hasn't got 'GROUP BY' just try and use the old method
*/
$stateGroup = $this->select->getRawState('group');
if( ! isset($stateGroup) || empty($stateGroup)) {
return parent::count();
}
$select = clone $this->select;
$select->reset(Select::LIMIT);
$select->reset(Select::OFFSET);
$statement = $this->sql->prepareStatementForSqlObject($select);
$result = $statement->execute();
$this->rowCount = $result->count();
return $this->rowCount;
}
Related
I have to merge results from multiple tables in Laravel. Currently I am using the following way:
public function getMachines(Request $request) {
$vendor_id = Auth::id();
$ml_8_machine=ML8Machine::where('vendor_id','=',$vendor_id)->get();
$ml_16_machine=ML16Machine::where('vendor_id','=',$vendor_id)->get();
$ml_32_machine=ML32Machine::where('vendor_id','=',$vendor_id)->get();
$ml_64_machine=ML64Machine::where('vendor_id','=',$vendor_id)->get();
$ml_96_machine=ML96Machine::where('vendor_id','=',$vendor_id)->get();
$ml_128_machine=ML128Machine::where('vendor_id','=',$vendor_id)->get();
$machines = collect($ml_8_machine)
->merge($ml_16_machine)
->merge($ml_32_machine)
->merge($ml_64_machine)
->merge($ml_96_machine)
->merge($ml_128_machine);
return view('vendor.machines', compact('machines'));
}
I am looking for an efficient way to do the query and merge result in one collection.
If your columns are the same,
you can use unionAll to merge the records. This will reduce the IO cost:
public function getMachines(Request $request) {
$vendor_id = Auth::id();
$ml_8_machine=ML8Machine::where('vendor_id','=',$vendor_id)->select('column1', 'column2'...);
$ml_16_machine=ML16Machine::where('vendor_id','=',$vendor_id)->select('column1', 'column2'...);
$ml_32_machine=ML32Machine::where('vendor_id','=',$vendor_id)->select('column1', 'column2'...);
$ml_64_machine=ML64Machine::where('vendor_id','=',$vendor_id)->select('column1', 'column2'...);
$ml_96_machine=ML96Machine::where('vendor_id','=',$vendor_id)->select('column1', 'column2'...);
$ml_128_machine=ML128Machine::where('vendor_id','=',$vendor_id)->select('column1', 'column2'...);
$machines = $ml_8_machine->unionAll($ml_16_machine)
->unionAll($ml_32_machine)
->unionAll($ml_64_machine)
->unionAll($ml_96_machine)
->unionAll($ml_128_machine)
->get();
return view('vendor.machines', compact('machines'));
}
Remember, you need to keep each columns as same order as others.
And you can add paginate() on this query too:
$machines = $ml_8_machine->unionAll($ml_16_machine)
->unionAll($ml_32_machine)
->unionAll($ml_64_machine)
->unionAll($ml_96_machine)
->unionAll($ml_128_machine)
->paginate(10);
maybe One of these ways may help you
$vendor_id = Auth::id();
$ml_8_machine=ML8Machine::where('vendor_id','=',$vendor_id)->get();
$ml_16_machine=ML16Machine::where('vendor_id','=',$vendor_id)->get();
$ml_32_machine=ML32Machine::where('vendor_id','=',$vendor_id)->get();
$ml_64_machine=ML64Machine::where('vendor_id','=',$vendor_id)->get();
$ml_96_machine=ML96Machine::where('vendor_id','=',$vendor_id)->get();
$machines = ML128Machine::where('vendor_id','=',$vendor_id)
->union($ml_8_machine)
->union($ml_16_machine)
->union($ml_32_machine)
->union($ml_64_machine)
->union($ml_96_machine)
->get();
return view('vendor.machines', compact('machines'));
or use raw query methods
(SELECT * from ml_8_machine where `vendor_id` = '?')
UNION
(SELECT * from ml_16_machine where `vendor_id` = '?')
or
SELECT * from ml_8_machine, ml_16_machine where `ml_8_machine.vendor_id` = '?' AND `ml_16_machine.vendor_id` = '?'
You may use collapse() method
$collection = collect([$ml_8_machine, $ml_16_machine, $ml_32_machine, $ml_64_machine, $ml_96_machine, $ml_128_machine]);
$machines = $collection->collapse();
//dump($machines);
return view('vendor.machines', compact('machines'));
I have a query:
User::selectRaw('users.facebook_id')
->join('orders', 'orders.customer_id', 'shop_users.id')
->groupBy('shop_users.id')
->havingRaw('SUM(orders.total) >= 0')
->get()->pluck('facebook_id')->all();
but I want it only for orders from the last 30 days from now.
I assume I could use something such as ->whereDate('created_at', '>', Carbon::now()->subDays(30)) but not sure how to apply this to orders.
I think where between is what you need
Here try this
User::selectRaw('users.facebook_id')
->join('orders', 'orders.customer_id', 'shop_users.id')
->groupBy('shop_users.id')
->havingRaw('SUM(orders.total) >= 0')
->whereBetween('orders.created_at', [Carbon::now()->addDays(-30), Carbon::now()])
->get()->pluck('facebook_id')->all();
This will work if your datatype is dateTime.
now if you want to date only you can use this ->toDateString() just add this on your carbon
Hope it helps.
Here is the Join function from Laravel Code base,
/**
* Add a join clause to the query.
*
* #param string $table
* #param \Closure|string $first
* #param string|null $operator
* #param string|null $second
* #param string $type
* #param bool $where
* #return $this
*/
public function join($table, $first, $operator = null, $second = null, $type = 'inner', $where = false)
{
$join = $this->newJoinClause($this, $type, $table);
// If the first "column" of the join is really a Closure instance the developer
// is trying to build a join with a complex "on" clause containing more than
// one condition, so we will add the join and call a Closure with the query.
if ($first instanceof Closure) {
call_user_func($first, $join);
$this->joins[] = $join;
$this->addBinding($join->getBindings(), 'join');
}
// If the column is simply a string, we can assume the join simply has a basic
// "on" clause with a single condition. So we will just build the join with
// this simple join clauses attached to it. There is not a join callback.
else {
$method = $where ? 'where' : 'on';
$this->joins[] = $join->$method($first, $operator, $second);
$this->addBinding($join->getBindings(), 'join');
}
return $this;
}
That means, you can do may be something like below
User::selectRaw('users.facebook_id')
->join('orders',function ($j) {
// Now $j is instance of builder e.g.
// $j->on('users.id', '=', 'orders.id')->orOn('users.name', '=', 'orders.name')->->whereDate('created_at', '>', Carbon::now()->subDays(30));
// Not super clear on the tables you are using
})
->groupBy('shop_users.id')
->havingRaw('SUM(orders.total) >= 0')
->get()->pluck('facebook_id')->all();
I am very confused about this (returning false):
$sql = "SELECT * from tbl_user WHERE group = 'abc'";
$res = mysql_query($sql);
if(mysql_num_rows($res) > 0) {
$response = array('status' => '1');
} else {
$response = array('status' => '0'); // ---> what I get back
die("Query failed");
}
...despite the fact the field group is present in mySQL database. Even more strange is that the following return the value of group:
$SQL = "SELECT * FROM tbl_user";
$result = mysql_query($SQL);
while ($db_field = mysql_fetch_assoc($result)) {
print $db_field['group']; // ---> returns 'abc'
When I execute a WHERE clause with every other fields of my table excepting group (for example WHERE name = 'ex1' AND ID=1 AND isAllowed=0 (and so on...), everything is fine. As soon as I insert group = 'abc', I get nothing...
This makes me mad. If anyone could help... (I am running a local server with MAMP).
Thanks a lot!
The issue is that group is a reserved word in SQL.
For MySql you need to escape it with backticks
`group`
So your query would be
$sql = "SELECT * from tbl_user WHERE `group` = 'abc'";
The following function (in my User Model) gives me the correct result for my chat system. Almost... I need to revert the order of the results.
public function getChatConv($cp, $page=1){
$limit = $page * 20;
$user = Authek::curUser();
$res = Chatmessage::where('receipient',$cp)->where('sender',$user->id)
->orWhere('receipient',$user->id)->where('sender',$cp)
->orderBy('created_at','desc')->take($limit)->get();
return $res;
}
It returns an object and I need an object as result. I tried already to convert the result to an array, revert the order and then convert the array back to object. This didn't work.
What I need is a nested query like the following raw SQL query:
SELECT *
FROM (
SELECT *
FROM chatmessages
WHERE (
receipient = '422'
AND sender = '22'
)
OR (
receipient = '22'
AND sender = '422'
)
ORDER BY created_at DESC
LIMIT 0 , 20
)faketable
ORDER BY created_at ASC
There are a few articles with nested queries, but I don't find a similar case and it would be good if someone could do this in Eloquent without the use of Raw queries... It must be possible.
Try this..
use take() and skip(),offset()
get 4 items from offset 3/4th:
Chatmessage::take(4)->offset(3)->get();
Or this (get 10 items from 8rd row):
Chatmessage::take(10)->skip(2)->get();
public function getChatConv($cp, $page=1){
$limit = $page * 20;
$user = Authek::curUser();
$res = Chatmessage::where('receipient',$cp)->where('sender',$user->id)
->orWhere('receipient',$user->id)->where('sender',$cp)
->orderBy('created_at','desc')->take(3)->skip(2)->get();
return $res;
}
Earlier this day a asked a question about an update query. But now i want to select some things ( and it is working ) but I also want to order them and put a limit on it.
This is the code to select all the food :
public function getFood($id)
{
$id = (int)$id;
$rowset = $this->tableGateway->select(array('kindOfFood_id' => $id));
$row = $rowset->current();
if (!$row) {
throw new \Exception("Could not find row $id");
}
return $row;
}
But how can i do this :
Select * from KindOfFood ==> order by kindOfFood_votes DESC ?
I saw on the documentation you can do something like this, but it doesn't work with me?
$rowset = $artistTable->select(function (Select $select) {
$select->where->like('name', 'Brit%');
$select->order('name ASC')->limit(2);
});
Are you looking to return only single row or multiple rows.
Try this for multiple rows -
use Zend\Db\Sql\Select; //at the top of the page among other use statements.
public function getFood($id)
{
$id = (int) $id;
$select = new Select(TABLE_NAME); //CHANGE TABLE_NAME as per needs
$select->where('kindOfFood_id = ' . $id);
$select->order('kindOfFood_votes DESC');
$resultSet = $this->tableGateway->selectWith($select); //Will get array of rows.
//$row = $rowset->current(); THIS IS FOR RETURNING ONLY SINGLE ROW NOT ALL ROWS
if (!$resultSet) {
throw new \Exception("Could not find rows with food id - $id");
}
return $resultSet;
}
Can access the returned resultSet via loop. Eg: foreach
foreach($resultSet as $row) {
echo $row->kindOfFood_id; //or something
}
Note:
If you need only
Select * from KindOfFood order by kindOfFood_votes DESC
then remove the $select->where('kindOfFood_id = ' . $id); line from above.