I am working on a project in Laravel and using DB facade to run raw queries of sql.
In my case I am using DB::select, problem is that pagination method is not working with this DB raw query and showing this error
Call to a member function paginate() on array
I just want how to implement laravel pagination with DB raw queries
here is my code:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Notice;
use Illuminate\Support\Facades\DB;
use Illuminate\Pagination\Paginator;
use Illuminate\Pagination\LengthAwarePaginator;
class NoticeController extends Controller
{
public function index(){
$notices = DB::select('select
notices.id,notices.title,notices.body,notices.created_at,notices.updated_at,
users.name,departments.department_name
FROM notices
INNER JOIN users ON notices.user_id = users.id
INNER JOIN departments on users.dpt_id = departments.id
ORDER BY users.id DESC')->paginate(20);
$result = new Paginator($notices,2,1,[]);
return view('welcome')->with('allNotices', $notices);
}
}
Try:
$notices = DB::table('notices')
->join('users', 'notices.user_id', '=', 'users.id')
->join('departments', 'users.dpt_id', '=', 'departments.id')
->select('notices.id', 'notices.title', 'notices.body', 'notices.created_at', 'notices.updated_at', 'users.name', 'departments.department_name')
->paginate(20);
public function index(Request $request){
$notices = DB::select('select notices.id,notices.title,notices.body,notices.created_at,notices.updated_at,
users.name,departments.department_name
FROM notices
INNER JOIN users ON notices.user_id = users.id
INNER JOIN departments on users.dpt_id = departments.id
ORDER BY users.id DESC');
$notices = $this->arrayPaginator($notices, $request);
return view('welcome')->with('allNotices', $notices);
}
public function arrayPaginator($array, $request)
{
$page = Input::get('page', 1);
$perPage = 10;
$offset = ($page * $perPage) - $perPage;
return new LengthAwarePaginator(array_slice($array, $offset, $perPage, true), count($array), $perPage, $page,
['path' => $request->url(), 'query' => $request->query()]);
}
Don't ever use the pagination logic on php-side!
Use limit and offset on your sql's and leave the rest to the database server.
Additional use a seperate count-select for your statement.
Count:
$sql_count = 'SELECT count(1) cnt FROM ('. $sql . ') x';
$result = \DB::select( DB::raw($sql_count) );
$data['count'] = $result[0]->cnt;
Results:
$sql .= ' LIMIT ' . $offset . ', ' . $limit;
$result = \DB::select( DB::raw($sql) );
$myPaginator = new \Illuminate\Pagination\LengthAwarePaginator($result, $data['count'], $limit, $page, ['path' => action('MyController#index')]);
$data['result'] = $result;
This is suitable for me
$sql = "some sql code";
$page = 1;
$size = 10;
$data = DB::select($sql);
$collect = collect($data);
$paginationData = new LengthAwarePaginator(
$collect->forPage($page, $size),
$collect->count(),
$size,
$page
);
It work for me, see
First use in your controller
use Illuminate\Pagination\Paginator;
then in function
$query = DB::select(DB::raw("SELECT pro.* , (SELECT TIMESTAMPDIFF(DAY,updated_at,'$current_date') from users as u where u.id=pro.id) as days FROM users as pro where role_id = 6 and delete_status=0 and user_status = 'A' and approved_status = 1 and is_clever_courier = 1 having days >= 5"));
$page1 = new Paginator($query, $maxPage);
dd($page1);
o/p =>
Paginator {#1450 ▼
#hasMore: true
#items: Collection {#1509 ▼
#items: array:10 [▼
0 => {#1454 ▶}
1 => {#1455 ▶}
2 => {#1456 ▶}
3 => {#1457 ▶}
4 => {#1458 ▶}
5 => {#1459 ▶}
6 => {#1460 ▶}
7 => {#1461 ▶}
8 => {#1462 ▶}
9 => {#1463 ▶}
]
}
#perPage: 10
#currentPage: 1
#path: "/"
#query: []
#fragment: null
#pageName: "page"
+onEachSide: 3
#options: []
After trying so many things I found the solution and it works fine for me, maybe it will be helpful for someone else.
First, in a PHP class use Illuminate\Pagination\LengthAwarePaginator:
use Illuminate\Pagination\LengthAwarePaginator;
public function index(Request $request) {
$notices = DB::select('SELECT notices.id, notices.title, notices.body, notices.created_at, notices.updated_at, users.name,departments.department_name
FROM notices
INNER JOIN users ON notices.user_id = users.id
INNER JOIN departments on users.dpt_id = departments.id
ORDER BY users.id DESC');
$notices = $this->arrayPaginator($notices, $request);
return view('welcome')->with('allNotices', $notices);
}
public function arrayPaginator($array, $request) {
$page = Input::get('page', 1);
$perPage = 10;
$offset = ($page * $perPage) - $perPage;
return new LengthAwarePaginator(
array_slice(
$array,
$offset,
$perPage,
true
),
count($array),
$perPage,
$page,
['path' => $request->url(), 'query' => $request->query()]
);
}
this work for me ... and good for performance :
query for count all results.
query for main query with offset and limit.
make paginate for current items based on page number.
$sql_count = "select count(*) as aggregate from `products`";
$data_count = DB::select($sql_count);
$count = $data_count[0]->aggregate;
$per_page =10; //define how many items for a page
$pages = ceil($count/$per_page);
$page = ($request->page=="") ?"1" :$request->page;
$start = ($page - 1) * $per_page;
$sql = "select * from `products`";
$sql.= ' LIMIT ' . $start . ', ' . $per_page;
$page = 1;
$size = 10;
$data = DB::select($sql);
$data = $this->paginate($data , $count , $per_page , $request->page);
public function paginate($items, $total , $perPage = 5, $page = null, $options = [])
{
$page = $page ?: (Paginator::resolveCurrentPage() ?: 1);
$items = $items instanceof Collection ? $items : Collection::make($items);
return new LengthAwarePaginator($items, $total, $perPage, $page, $options);
}
Related
I have $slug variable that takes frontend url slug value and sends it to model scope. Inside scope if I use echo $slug it works and also it works inside query (I have it to display products that have only same category slug).
Query where $slug works:
return $query->orwhereHas('category', function($q) use ($slug){
$q->where('slug', '=', $slug);
})->paginate($perPage, $page);
Here is the SQL:
select * from `slasher_farming_mods` where exists (select * from `slasher_farming_categories` where `slasher_farming_mods`.`category_id` = `slasher_farming_categories`.`id` and `slug` = 'medium-tractors') limit 4 offset 0
As visible slug value is "medium-tractors".
If I try to use $slug inside sort loop or inside brands loop it becomes empty
public function scopeListFrontEnd($query, $options = [], $slug = ''){
extract(array_merge([
'page' => 1,
'perPage' => 4,
'sort' => 'brandName desc',
'brands' => null,
'categorySlug' => null,
], $options));
print $slug; //This works here
if(!is_array($sort)) {
$sort = [$sort];
}
foreach ($sort as $_sort){
if(in_array($_sort, array_keys(self::$allowedSortingOptions))){
$parts = explode(' ', $_sort);
if(count($parts) < 2){
array_push($parts, 'desc');
}
list($sortField, $sortDirection) = $parts;
$query->orderBy($sortField, $sortDirection);
}
}
if($brands !== null) {
if(!is_array($brands)){
$brands = [$brands];
}
foreach ($brands as $brand){
$query->orwhereHas('brand', function($q) use ($brand){
$q->where('brand_id', '=', $brand);
});
}
}
$lastPage = $query->paginate($perPage, $page)->lastPage();
if($lastPage < $page) {
$page = 1;
}
return $query->orwhereHas('category', function($q) use ($slug){
$q->where('slug', '=', $slug);
})->paginate($perPage, $page);
}
Example SQL when using brands filter
select * from `slasher_farming_mods` where exists (select * from `slasher_farming_brands` where `slasher_farming_mods`.`brand_id` = `slasher_farming_brands`.`id` and `brand_id` = '5') or exists (select * from `slasher_farming_categories` where `slasher_farming_mods`.`category_id` = `slasher_farming_categories`.`id` and `slug` = '') order by `name` desc limit 4 offset 0
Slug variable becomes empty.
How could I keep slug variable data inside brand loop or inside sort loop?
I'm trying to get all videos from a DB that contain a keyword, It's an API REST building with Symfony 3. Here is a sample of the URL
http://localhost/Server/symfony/web/app_dev.php/video/search/prueba
Where "prueba" is the keyword that i'm trying to find
Here is the function..
public function searchAction(Request $request, $search = null){
$helper = $this->get("app.helper");
$em = $this->getDoctrine()->getManager();
if($search != null){
$dql = "SELECT v.title FROM BackendBundle:Video v "
. "WHERE v.title LIKE :search OR "
. "v.description LIKE :search ORDER BY v.id DESC";
$query = $em->createQuery($dql)
->setParameter("search", "%search%");
} else {
$dql = "SELECT v FROM BackendBundle:Video v ORDER BY v.id DESC";
$query = $em->createQuery($dql);
}
$page = $request->query->getInt("page", 1);
$paginator = $this->get("knp_paginator");
$items_per_page = 6;
$pagination = $paginator->paginate($query, $page, $items_per_page);
$total_items_count = $pagination->getTotalItemCount();
$data = array(
"status" => "success",
"total_items_count" => $total_items_count,
"page_actual" => $page,
"items_per_page" => $items_per_page,
"total_pages" => ceil($total_items_count/$items_per_page),
"data" => $pagination
);
return $helper->toJson($data);
}
The result should be a JSON with 4 videos.. but I get
{
"status": "success",
"total_items_count": 0,
"page_actual": 1,
"items_per_page": 6,
"total_pages": 0,
"data": []
}
where "data" is an array with the video data
Somebody know what I miss? the dql consult.. it's rigth?
Somebody could tell me what is the mistake? I need a clue.. the dql consult is correct?
I see you use knp_paginator service. Try to get items from paginator like this:
$data = array(
"status" => "success",
"total_items_count" => $total_items_count,
"page_actual" => $page,
"items_per_page" => $items_per_page,
"total_pages" => ceil($total_items_count/$items_per_page),
"data" => $pagination->getItems()
);
Thank you for your replay guys!!
So.. I made a mistake while typing.. and I forgot sign $ before the search word..
thats was the probllem
$query = $em->createQuery($dql)
->setParameter("search", "%search%"); <-- here
Thank you so much!!
I build a custom query and tried use the default paginator, like this:
WodsController.php
$userId = $this->Auth->user('id');
$connection = ConnectionManager::get('default');
$result = $connection->execute("SELECT wods.id, wods.titulo , wods.dia , wods.tempo, wods.repeticoes ,userwods.user_id FROM wods
LEFT JOIN userwods ON userwods.wod_id = wods.id WHERE userwods.user_id is null or userwods.user_id=4 order by wods.dia desc limit 50")->fetchAll('assoc');
$results = array();
foreach ($result as $r) {
$entity = $this->Wods->newEntity($r);
array_push($results, $entity);
}
$wods = $this->paginate($results);
$this->set('_serialize', ['wods']);
I got this error "Unable to locate an object compatible with paginate".
Now I'm tryng implement custom query paginator, but it's not working.
I implemented paginate and paginateCount functions in the model.
Wods.php file:
public function paginate($conditions, $fields, $order, $limit, $page = 1, $recursive = null, $extra = array()) {
$recursive = -1;
$this->useTable = false;
$sql = '';
$sql .= "SELECT wods.id, wods.titulo , wods.dia , wods.tempo, wods.repeticoes ,userwods.user_id FROM wods LEFT JOIN userwods ON userwods.wod_id = wods.id WHERE userwods.user_id is null or userwods.user_id=4 order by wods.dia desc limit ";
// Adding LIMIT Clause
$sql .= (($page - 1) * $limit) . ', ' . $limit;
$results = $this->query($sql);
return $results;
}
public function paginateCount($conditions = null, $recursive = 0, $extra = array()) {
$sql = '';
$sql .= "SELECT wods.id, wods.titulo , wods.dia , wods.tempo, wods.repeticoes ,userwods.user_id FROM wods LEFT JOIN userwods ON userwods.wod_id = wods.id WHERE userwods.user_id is null or userwods.user_id=4 order by wods.dia desc";
$this->recursive = $recursive;
$results = $this->query($sql);
return count($results);
}
In the controller WodsController.php
public function index()
{
$this->Wods->recursive = 0;
$this->paginate = array('Wods'=>array('limit'=>10));
$this->set('wods', $this->paginate('Wods'));
}
But the custom paginator is not called, it continues calling the default paginate function. Why ?
Following dragmosh advise (thanks), I investigate CakePHP ORM custom queries builder.
In this solution I used find() function with specific options, after I called the default paginator:
$query = $this->Wods->find()
->select(['Wods.id', 'Wods.titulo','Wods.dia','Wods.rounds','Wods.tempo','Wods.repeticoes','Userwods.user_id'])
->join([
'table' => 'Userwods',
'alias' => 'Userwods',
'type' => 'LEFT',
'conditions' => 'Userwods.wod_id = Wods.id',
])
->where(function ($exp, $q) {
return $exp->isNull('Userwods.user_id');})
->orWhere(['Userwods.user_id' => 4])
->contain(['Userwods'])
->autoFields(true);
$wods = $this->paginate($query);
$this->set(compact('wods'));
$this->set('_serialize', ['wods']);
I'm making a Laravel Pagination based from my query result and be rendered in my view. I'm following this guide http://laravel.com/docs/5.1/pagination but I get an error:
Call to a member function paginate() on a non-object
I'm using query builder so I think that should be ok? Here's my code
public function getDeliveries($date_from, $date_to)
{
$query = "Select order_confirmation.oc_number as oc,
order_confirmation.count as cnt,
order_confirmation.status as stat,
order_confirmation.po_number as pon,
order_summary.date_delivered as dd,
order_summary.delivery_quantity as dq,
order_summary.is_invoiced as iin,
order_summary.filename as fn,
order_summary.invoice_number as inum,
order_summary.oc_idfk as ocidfk,
order_summary.date_invoiced as di
FROM
order_confirmation,order_summary
where order_confirmation.id = order_summary.oc_idfk";
if (isset($date_from)) {
if (!empty($date_from))
{
$query .= " and order_summary.date_delivered >= '".$date_from."'";
}
}
if (isset($date_to)) {
if (!empty($date_to))
{
$query .= " and order_summary.date_delivered <= '".$date_to."'";
}
}
$query.="order by order_confirmation.id ASC";
$data = DB::connection('qds106')->select($query)->paginate(15);
return $data;
}
However when I remove the paginate(15); it works fine.
Thanks
in the doc at this page: http://laravel.com/docs/5.1/pagination
we can see that we are not forced to use eloquent.
$users = DB::table('users')->paginate(15);
but, be sure you don't make a groupBy in your query because, the paginate method uses it.
after i'm no sure you can use paginate with query builder ( select($query) )
--- edit
You can create collection an use the paginator class :
$collection = new Collection($put_your_array_here);
// Paginate
$perPage = 10; // Item per page
$currentPage = Input::get('page') - 1; // url.com/test?page=2
$pagedData = $collection->slice($currentPage * $perPage, $perPage)->all();
$collection= Paginator::make($pagedData, count($collection), $perPage);
and in your view just use $collection->render();
public function getDeliveries($date_from, $date_to)
{
$query="your_query_here";
$deliveries = DB::select($query);
$deliveries = collect($deliveries);
$perPage = 10;
$currentPage = \Input::get('page') ?: 1;
$slice_init = ($currentPage == 1) ? 0 : (($currentPage*$perPage)-$perPage);
$pagedData = $users->slice($slice_init, $perPage)->all();
$deliveries = new LengthAwarePaginator($pagedData, count($deliveries), $perPage, $currentPage);
$deliveries ->setPath('set_your_link_page');
return $deliveries;
}
You set it by using the custom pagination..
$query = "Your Query here";
$page = 1;
$perPage = 5;
$query = DB::select($query);
$currentPage = Input::get('page', 1) - 1;
$pagedData = array_slice($query, $currentPage * $perPage, $perPage);
$query = new Paginator($pagedData, count($query), $perPage);
$query->setPath('Your Url');
$this->data['query'] = $query;
return view('Your_view_file', $this->data, compact('query'));
Here you can specify the path by using the setpath().
In your View
#foreach($query as $rev)
//Contents
#endforeach
<?php echo $Reviews->appends($_REQUEST)->render(); ?>
The appends will append the data.
Thank you.
this is the way i did, it use query builder and get the same result with pagination
$paginateNumber = 20;
$key = $this->removeAccents(strip_tags(trim($request->input('search_key', ''))));
$package_id = (int)$request->input('package_id', 0);
$movieHasTrailer = MovieTrailer::select('movie_id')->where('status','!=','-1')->distinct('movie_id')->get();
$movieIds = array();
foreach ($movieHasTrailer as $index => $value) {
$movieIds[] = $value->movie_id;
}
$keyparams = array();
$packages = Package::select('package_name','id')->get();
$whereClause = [
['movie.status', '!=', '-1'],
['movie_trailers.status', '!=', '-1']
];
if(!empty($key)){
$whereClause[] = ['movie.title', 'like', '%'.$key.'%'];
$keyparams['search_key'] = $key;
}
if($package_id !== 0){
$whereClause[] = ['movie.package_id', '=', $package_id];
$keyparams['package_id'] = $package_id;
}
$movies = DB::table('movie')
->leftJoin('movie_package','movie.package_id','=','movie_package.id')
->leftJoin('movie_trailers','movie.id','=','movie_trailers.movie_id')
->where($whereClause)
->whereIn('movie.id',$movieIds)
->select('movie.*','movie_package.package_name','movie_trailers.movie_id as movie_id',
DB::raw('count(*) as total_trailers, movie_id')
)
->groupBy('movie.id')
->paginate($paginateNumber);
If you need to hydrate, you can do this...
$pages = DB::table('stuff')
->distinct()
->paginate(24, ['stuff.id']);
$stuffs = Stuff::hydrate($pages->items());
return view('stuff.index')->with('stuffs', $stuffs)->with('pages', $pages)
$stuffs will contain your model object, $pages will contain your pagination, probably not the most efficient, but it works.
// import those Class into your Laravel Controller
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\Input;
use Illuminate\Database\Eloquent\Collection;
// In your public function
$query = DB::select(DB::raw("SELECT * FROM your_table"));
$collection = new Collection($query);
// Paginate
$perPage = 10; // Item per page
$currentPage = Input::get('page') - 1;
$pagedData = $collection->slice($currentPage * $perPage, $perPage)->all();
$pagination = new Paginator($pagedData, $perPage);
return response()->json([
$pagination
], 200);
I have a controller with a method that looks something like this:
public function browsecategory($category_id)
{
//find any subcategories for this category
$this->load->model('category/category_model');
$this->load->model('category/product_category_model');
$records['categories'] = $this->category_model->find_all_by('parent_id', $category_id);
//add some product data too.
$records['products'] = $this->product_category_model->find_all_by('category_id', $category_id);
Template::set('records', $records);
Template::render();
}//end browsecategory
All the examples I've seen for the codeigniter pagination "stuff" is using one query.
I need to combine two data sets and serve on one view.
Any suggestions?
EDIT 1
I've tried to follow MDeSilva's suggestion below. And although the pagination object is correctly calculating the number of links to create, all items appear on all pages.
Here's the code in the model that gets the data:
public function get_categories_and_products($limit=12, $offset=0, $category_id=null)
{
print '<BR>the function got the following offeset:'.$offset;
$query = "(SELECT cat.category_id, cat.title, cat.image_thumb, cat.deleted, cat.display_weight ";
$query = $query."FROM bf_categories cat ";
$query = $query."WHERE cat.parent_id=".$category_id;
$query = $query." AND cat.category_id <>".$category_id;
$query = $query.") UNION (";
$query = $query."SELECT p.product_id, p.name, p.image_thumb, p.deleted , p.display_weight";
$query = $query." FROM bf_product p ";
$query = $query."Inner join bf_product_category cp ";
$query = $query."on p.product_id=cp.product_id ";
$query = $query."Where cp.category_id=".$category_id.")";
$this->db->limit($limit, $offset);
$catsandprods= $this->db->query($query);
return $catsandprods->result() ;
}
And here's the code in the controller:
public function browsecategory($category_id, $offset=0)
{
$this->load->library('pagination');
$total = $this->product_model->get_cats_prods_count($category_id);
$config['base_url'] = site_url('/product/browsecategory/'.$category_id);
$config['uri_segment'] = 4;
$config['total_rows'] = $total;
$config['per_page'] = 5;
$config['num_links'] = 10;
$this->pagination->initialize($config);
$offset = ($this->uri->segment($config['uri_segment'])) ? $this->uri->segment($config['uri_segment']) : 0;
print $offset;
//Call the model function here to get the result,
$records= $this->product_model->get_categories_and_products(5,$offset,$category_id);
//add to breadcrumb trail
$this->build_bread_crumb_trail($category_id);
$breadcrumbs = $this->breadcrumbs->expand_to_hyperlinks();
Template::set('currentcategory',$category_id);
Template::set('breadcrumbs', $breadcrumbs);
Template::set('records', $records);
Template::render();
}
I've debugged and I can see that the line of code "$this->db->limit($limit, $offset);" in the model is not working. It always returns the full record set...
Can you tell me what I'm missing?
Thanks.
This is the way to generate pagination links in CI, for your requirement have a query with a join,
public function index($offset = 0) {
$language_id = 1;
$artwork_id = null;
if ($_SERVER['REQUEST_METHOD'] == 'POST')
{
$artwork_id = $this->input->post('serach_artwork_id', TRUE) ? $this->input->post('serach_artwork_id', TRUE) : null;
$data['artwork_id'] = $artwork_id;
}
$this->load->library('pagination');
$limit = 10;
$total = $this->Artwork_model->get_artwork_count($language_id, $artwork_id);
$config['base_url'] = base_url().'artwork/index/';
$config['total_rows'] = $total;
$config['per_page'] = $limit;
$config['uri_segment'] = 3;
$config['first_link'] = '<< First';
$config['last_link'] = 'Last >>';
$config['next_link'] = 'Next ' . '>';
$config['prev_link'] = '<' . ' Previous';
$config['num_tag_open'] = '<span class="number">';
$config['num_tag_close'] = '</span>';
$config['cur_tag_open'] = '<span class="current"><a href="#">';
$config['cur_tag_close'] = '</a></span>';
$this->pagination->initialize($config);
//Call the model function here to get the result,
$data['artworks'] = $this->Artwork_model->get_artworks($language_id, $limit, $offset, $artwork_id);
$this->template->write('title', 'Artwork : Manage Artwork');
$this->template->write_view('content', 'artwork/index', $data);
$this->template->render();
}
Here is an example for query with multiple joins in the model
public function get_artworks($language_id = 1, $limit = 10, $offset = 0, $arwork_id = null)
{
$this->db->select('a.id, a.price, a.is_shop, at.title,at.status,at.date_added,ats.name as artist_name');
$this->db->from('artworks a');
$this->db->join('artwork_translations at', 'a.id = at.artwork_id');
$this->db->join('artists ats', 'a.artist_id = ats.id');
$this->db->where('at.language_id', $language_id);
if(!is_null($arwork_id) && !empty($arwork_id) && $arwork_id != 'all')
{
$this->db->where('a.id =', $arwork_id);
}
$this->db->order_by('a.id DESC');
$this->db->limit($limit, $offset);
$artworks = $this->db->get();
return $artworks->result();
}
In the View
<?= $this->pagination->create_links(); ?>
The pagination class doesn't care about the how the data source is constructed, just so you hand it the data result object it wants. So you would just pass limits & offsets into your data queries, or else pull all your data and slice it up afterwards.
However, I don't really understand how you are thinking to combine these different bits of data into a single result to display - are you trying to display all categories, then paginate the products? If so, you are set up incorrectly
Simply use PHP function array_merge
<?php $beginning = 'foo'; $end = array(1 => 'bar');
$result = array_merge((array)$beginning, (array)$end);
$this->load->view('view.php', $result);
?>
and extract according to keys for array used.
It is really simple & working