How can i deploy pagination after foreach loop in laravel - php

I'm trying to create a pagination in my search result. I have one queries in my repository like:
public function getFilteredVehicles($id,$pax,$categories,$search_type,$from_date,$to_date)
{
$vehicles = Vehicle::whereHas('destinations', function($q) use($id){
$q->where('destination_id',$id)
->where('active',1);
})
->paginate(1);
if($search_type == 'disposal' || $search_type == 'pnd')
{
$vehicles = $vehicles->where('registered_location',$id);
}
if($categories)
{
$vehicles = $vehicles->where('vehicle_categoryid',$categories);
}
return $vehicles;
}
The returned result again needs to be processed via loop like:
public function calculateLocationAmount($vehicles,$fdate,$tdate,$destination_id)
{
$datetime1 = new DateTime($fdate);
$datetime2 = new DateTime($tdate);
$interval = $datetime1->diff($datetime2);
$days = $interval->format('%a');
$days = $days+1;
$nights = $days-1;
foreach ($vehicles as $key => $vehicle) {
# code...
$perday_rate = $vehicle->destinations->where('id',$destination_id)->first()->pivot->day_rate;
$pernight_rate = $vehicle->destinations->where('id',$destination_id)->first()->pivot->night_rate;
$day_rate = $perday_rate * $days;
$night_rate = $pernight_rate * $nights;
$total_amount = $day_rate + $night_rate;
$vehicle['total_amount'] = $total_amount;
$vehicle['availability'] = 'true';
if($vehicle->whereHas('unavailability', function($q) use($fdate,$tdate){
$q->whereRaw("? BETWEEN `from_date` AND `to_date`", [$fdate])
->orwhereRaw("? BETWEEN `from_date` AND `to_date`", [$tdate]);
})->count()>0){
$vehicle['availability'] = 'false';
}
}
return $vehicles;
}
This final result needs to be paginated. How can i do it?
Using foreach is changing the value to collection due to which links is not working. If i don't do paginate() or get(), for loop is not executed.
Kindly help.

You can paginate your initial query just as you have, then loop over the pagination object as if it was your regular collection or use $pagination->items()
You should also use nested with('relation') in your initial query to stop N+1 queries https://laravel.com/docs/8.x/eloquent-relationships#constraining-eager-loads

Related

Laravel 5.4: slow loading

I have problem with slow load: almost 10 seconds. I guess I have to optimize the code in some way, but I don't know how. Any help? Thanks.
This is the controller. I use the trait ListNoticias (below the controller).
class NoticiaController extends Controller
{
use ListNoticias;
public function actualidad(Request $request)
{
$data['section_id'] = explode(',', '1,2,3');
$data['ruta'] = 'actualidad';
$data['title'] = __('header.actualidad');
$data['num'] = $request->num;
$url = $request->url();
$data = $this->listNoticias($data, $url);
return view('web.actualidad.listado', compact('data'));
}
}
And this is the trait. Here, I collect a list of all the news in three different arrays for each of the languages and then I manually paginate them.
trait ListNoticias
{
public function listNoticias($data, $url)
{
$now = date('Y-m-d');
$time = date('H:i:s');
(isset($data['num']))? $num = $data['num'] : $num = '15';
$data['images'] = Image::where('imageable_type', 'App\Models\Noticia')->get();
$data['sections'] = Section::all();
$data['noticias'] = Noticia::where('date', '<', $now)
->where('active', '1')
->whereIn('section_id', $data['section_id'])
->orWhere('date', '=', $now)
->where('time', '<=', $time)
->where('active', '1')
->whereIn('section_id', $data['section_id'])
->orderBy('date', 'desc')
->orderBy('time', 'desc')
->get();
$data['noticias-es'] = [];
$data['noticias-en'] = [];
$data['noticias-pt'] = [];
foreach($data['noticias'] as $row){
foreach($row->langs as $row_lang) {
if ($row_lang->lang_id == '1') {
$data['noticias-es'][] = $row;
} elseif ($row_lang->lang_id == '2') {
$data['noticias-en'][] = $row;
} elseif ($row_lang->lang_id == '3') {
$data['noticias-pt'][] = $row;
} else null;
}
}
// Manual paginate
/* Get current page form url e.x. &page=1
Create a new Laravel collection from the array data
Slice the collection to get the items to display in current page
Create our paginator and pass it to the view
set url path for generated links
*/
$currentPage = LengthAwarePaginator::resolveCurrentPage();
// ES
$itemCollection = collect($data['noticias-es']);
$currentPageItems = $itemCollection->slice(($currentPage * $num) - $num, $num)->all();
$data['noticias-es'] = new LengthAwarePaginator($currentPageItems , count($itemCollection), $num);
$data['noticias-es']->setPath($url);
// EN
$itemCollection = collect($data['noticias-en']);
$currentPageItems = $itemCollection->slice(($currentPage * $num) - $num, $num)->all();
$data['noticias-en'] = new LengthAwarePaginator($currentPageItems , count($itemCollection), $num);
$data['noticias-en']->setPath($url);
// PT
$itemCollection = collect($data['noticias-pt']);
$currentPageItems = $itemCollection->slice(($currentPage * $num) - $num, $num)->all();
$data['noticias-pt'] = new LengthAwarePaginator($currentPageItems , count($itemCollection), $num);
$data['noticias-pt']->setPath($url);
return $data;
}
}
EDITED WITH MORE INFO
Following the advice of the comments, I found one of the main problems in the following view, where I call the images of each news item to show it in the list along with the url and the slug. This I have done creating a foreach inside another. Is there any way to do it without nesting foreachs? Thank you.
The partial view with the image and url and slug:
#foreach($new->langs as $new_lang)
#if($new_lang->lang_id == $lang_id)
#foreach($data['images'] as $image)
#if($image->imageable_id == $new->id && $image->main == '1')
#php
$mini = substr($image->path, 0, strrpos( $image->path, "/"));
$name = substr($image->path, strrpos($image->path, '/') + 1);
$image_mini = $mini.'/mini-'.$name;
#endphp
<div class="crop">
<a href="{{ route('noticia', [$new->id, $new_lang->slug]) }}">
{{ HTML::image(file_exists($image_mini)? $image_mini : $image->path, '', array('class' => 'img-responsive ancho_100')) }}
</a>
</div>
#endif
#endforeach
#endif
#endforeach

Laravel - Model filter date field by month

I have a method that can receive a parameter of "month" and another of "year", you can receive both or only one of them.
In the case of only receiving the month I want to make a query that I look for in a field that is called "created_at" just looking for the month of that field date
At the moment I use this code but when doing a like it does not do it correctly
else if ($request->has('month')) {
$month = $request->input('month');
$currentTimestamp = date_create_from_format('n', $month)->getTimestamp();
$currentDate = date('m', $currentTimestamp);
$filters['updated_at'] = $currentDate;
unset($filters['month']);
}
$cars = Car::where(function($query) use($filters) {
foreach ($filters as $column => $value) {
if ($column === 'updated_at') {
$query->where($column, 'LIKE', '%'.$value.'%');
continue;
}
$query->where($column, 'LIKE', '%'.$value.'%');
}
})->orderBy('updated_at', 'desc')->get();
You should try this:
if ($request->has('month')) {
$month = $request->input('month');
Car::whereRaw('MONTH(created_at) = '.$month)->get();
}
You can use whereMonth method, like this:
$cars = Car::whereMonth('created_at', '12')->get();
Example with determining if a month value is exist:
if ($request->has('month')) {
$cars = Car::whereMonth('created_at', $request->input('month'))->get();
}
You can use laravel queryBuilder .
The idea is pretty simple. Build your query and execute it only when needed
carQuery = Car::newQuery();
if ($request->has('month') {
carQuery->where('created_at' .. do camparison you want);
}
if ( $request->has('year') {
// add extra query filter
}
cars = carQuery->get();

Laravel 5 Pagination with Query Builder

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);

Laravel - Counting from third relationship

I have the following code:
$main = Main::with(['clients', 'events.orderitems' => function($query) {
$query->whereIn('order_id', function($query) {
$query->select('id')->from('orders')->where('orderPaid', 1)->orWhere('orderStatus', 3);
});
}])->where('id', $id)->first();
foreach($main->events as $dates) {
$all_paid = 0;
$all_pending = 0;
foreach($dates->orderitems as $item) {
if($item->orderPaid == 1) {
$all_paid = $all_paid + $item->quantity;
}
}
$dates->orderscount = $all_paid;
foreach($dates->orderitems as $item) {
if($item->orderStatus == 3) {
$all_pending = $all_pending + $item->quantity;
}
}
$dates->pendingcount = $all_pending;
}
Is there maybe an MYSQL way to Count the PAID orders and the orders with orderStatus == 3 in the SQL? I think, how I am doing it, it's way to messy and not very good for the performance.
So a "Main" has n-events which have n-orderItems.
I need to get to a "Main" Event, all the Events with all PAID and orderStatus == 3 items. How can I do that?
UPDATE - SOLUTION:
foreach($main->events as $dates) {
$dates->orderscount = OrderItems::where('events_id',$dates->id)->whereHas('orders', function($q) {
$q->where('orderPaid', 1);
})->sum('quantity');
$dates->pendingcount = OrderItems::where('events_id',$dates->id)->whereHas('orders', function($q) {
$q->where('orderStatus', 3);
})->sum('quantity');
}
Laravel has an aggregate method which does just that.
The same way you are using first to only get the first result of a query as in:
)->where('id', $id)->first();
to get the count you could use count()
DB::table('orders')->where('orderPaid', 1)->orWhere('orderStatus', 3)->count();
If you need the sum() of the products and not the count as in your title, you could for your particular example use:
$total_quantity = DB::table('orders')->where('orderPaid', 1)->orWhere('orderStatus', 3)->sum('quantity');
UPDATE:
As in your example to get both counts you would change you foreach to be something similar to:
foreach($main->events as $dates) {
$all_paid = DB::table('orders')->where('order_id',$dates->id)->where('orderPaid', 1)->count();
$all_pending = DB::table('orders')->where('order_id',$dates->id)->where('orderStatus', 3)->count();
$dates->orderscount = $all_paid;
$dates->pendingcount = $all_pending;
}
You can use sum() with your eloquent query in laravel. Check under the #Aggregates section here:
http://laravel.com/docs/4.2/queries

I want to take a single value from one function and use it in another function - codeigniter/php/mysql

I want to take a value from a query in one function and use it for multiplication within another function.
Here i have a function producing the number 3:
function insert() {
$siteid = 1;
$field = 3;
$time = "2011-10-11 15:04:56";
$this->db->select('Offset_value', 1)
->from('offset')
->where('siteid', $siteid)
->where('Offset_field', $field)
->where('time <', $time);
$query = $this->db->get()->result_array();
foreach ($query as $row) {
echo $row['Offset_value'];
}
return $query;
}
At the moment this is just set to display the number 3 but i need that value to be stored in a variable of some sort and load it into this function:
function get_sap_estimate() {
$kwp = 5;
$direction = 34;
$tilt = 60;
$month = 4;
$sap_shadingid = 2;
$this->db->select('derating_factor')
->from('sap_shading')
->where('id', $sap_shadingid);
$query = $this->db->get()->result_array();
$query = $query[0];
$dr = $query['derating_factor'];
$this->db->select("kwh * $dr * $kwp AS kwhdata")
->from('expected_kwh')
->where('direction', $direction)
->where('tilt', $tilt)
->where('month', $month);
$query2 = $this->db->get()->result_array();
return $query2;
}
Can this be done and how??
Thanks
function A(){
return "value";
}
functionB(){
var value = A(); // here is your array
var value_you_need = value[0]['Offset_value'];
}
If they are both in the same class (read: model file) write
var $_var;
before any methods are made. Then inside one of your methods type
$this->_var = $stuff;
to give this var a value. Then to use that value type
$new_stuff = $this->_var;
If they're not in the same class, you could try calling the first method inside of the second.

Categories