I'm making a table of orders that have a specific current step in this case the step is 'NESTEN' and some other where statements to get the orders back that I need.
Because I don't want a list with more than 400 orders on 1 page I want to use laravels pagination functionality that also give a performance boost on the list of more than 100.000 records. The laravel pagination works as is should but the problem comes when I want to use the filter I made.
I made a dropdown list that needs to filter based on material of the orders that are in the list. In the table I see the filtered orders but the pagination has the same page count and order count as before. So to the point, the problem is that the pagination is not updating after filtering the collection from the query.
What I already tried was some googling and found several solutions that didn't solve my problem or even caused more problems...
Also added a $orders->filter function to remove orders that don't meet up the filter requirements but no result...
To keep it understandable for now I added the code in the Route file.
My route is as followed
Route::get('orders/nesten', function(Request $request) {
$orders = ShopOrder::where([
['ItemCode', 'LIKE', 'CM%'],
['Exact', '=', null],
['Nesting', '=', null],
['IsOnHold', '!=', 1],
['ShopOrderRoutingStepPlanCount', '!=', 0]
])->paginate(50);
$filteredCollection = $orders->filter(function ($order) use($request) {
if($request->exists('material')) {
return $order->getCurrentStep() == 'Nesten'
&& $order->getMaterial() == $request->get('material');
}
return $order->getCurrentStep() == 'Nesten';
});
$orders->setCollection($filteredCollection);
return view('dashboard/actions/Nesten')->with('shopOrders', $orders);
});
In the ShopOrder Model I declared the functions ->getMaterial() and ->getCurrentStep() as
public function routingStepPlans() {
return $this->hasMany('App\Models\Exact\ShopOrder\RoutingStepPlan', 'ShopOrder', 'ID');
}
public function materialPlans() {
return $this->hasMany('App\Models\Exact\ShopOrder\MaterialPlan', 'ShopOrder', 'ID');
}
public function getCurrentStep() {
$current = $this->routingStepPlans()->where('LineNumber', ($this->timeTransactions()->count() + 1))->first();
if(isset($current->Description)) {
return $current->Description;
}
return 'Afgerond';
}
public function getMaterial() {
$material = $this->materialPlans()->where('ItemCode', 'LIKE', 'TAP%')->first();
if(isset($material->Description)) {
return $material->Description;
}
return '-';
}
and as last the view
<table class="table table-striped table-bordered no-width">
<thead>
<tr>
<th>Order nummer</th>
<th>SKU</th>
<th>Omschrijving</th>
<th>Aantal</th>
<th>Datum</th>
<th>Deadline</th>
<th>Materiaal</th>
<th>DXF?</th>
</tr>
</thead>
<tbody>
#foreach($shopOrders as $shopOrder)
<tr>
<td>{{ $shopOrder->ShopOrderNumber }}</td>
<td>{{ $shopOrder->ItemCode }}</td>
<td>{{ str_replace('Car Mats', '',$shopOrder->Description) }}</td>
<td>{{ $shopOrder->PlannedQuantity }}</td>
<td>{{ $shopOrder->PlannedStartDate }} </td>
<td>{{ $shopOrder->PlannedDate }}</td>
<td>{{ $shopOrder->getMaterial() }}</td>
<td>{{ $shopOrder->hasDxf() }}</td>
</tr>
#endforeach
</tbody>
</table>
{{ $shopOrders->total() }}
{{ $shopOrders->appends(['material' => Request::get('material')])->render() }}
I expect 1 page from pagination with orders/nesten?material=Saxony%20Zwart&page=1 as url since that material has 9 orders.
But currently it still has 151 pages, the same as just going to orders/nesten.
From what i see you're fetching all records from database and then filter them with Collection filter. Pagination is build from database query results, so you should update your query to do the filtering.
Update
Create abstract filter class
namespace App\Http\Filters;
use Illuminate\Http\Request;
use Illuminate\Database\Eloquent\Builder;
class AbstractFilter
{
protected $builder;
protected $request;
public function __construct(Request $request)
{
$this->request = $request;
}
public function apply(Builder $builder)
{
$this->builder = $builder;
foreach($this->filters() as $name => $value) {
if(method_exists($this, $name)) {
call_user_func_array([$this, $name], array_filter([$value]));
}
}
return $this->builder;
}
public function filters()
{
return $this->request->all();
}
}
Extend that class for your request (NOTE - method names here are the request parameters for filters):
namespace App\Http\Filters;
class OrdersFilter extends AbstractFilter
{
public function material($value = null)
{
if (!is_null($value)) {
// return modified query here (return $this->builder->where....)
}
}
}
Add scope to your model (in ShopOrder model):
public function scopeFilter($query, OrderFilters $filter) {
return $filter->apply($query);
}
Update your query like this:
$orders = ShopOrder::whereLike('ItemCode', 'CM%')
->whereNull('Exact')
->whereNull('Nesting')
->where('IsOnHold', '!=', 1)
->where('ShopOrderRoutingStepPlanCount', '!=', 0)
->filter(new OrdersFilter($request))
->paginate(50);
And you can skip all collection filtering. Also you can add more filter params to OrdersFilter class.
NOTE: i wrote everything without testing, so there might be some minor errors. Also I haven't wrote materials filter without having a database structure, but your Model methods could be better.
UPDATE
for filter materials method i would use something like this (not 100% if it works, haven't tested):
public function material($value = null) {
if (!is_null($material)) {
return $this->builder->having('', function($query) use ($value) {
return $query->where('ItemCode', 'LIKE', 'TAP%')
->where('description', '=', $value);
})
->having('routingStepPlans', function($query) {
return $query->where('LineNumber', ShopOrder::timeTransactions()->count() + 1)
->where('description', '=', 'Nesten');
});
}
}
so that your pagination continues but keep your search
{{ $shopOrders->appends(Request::except('page'))->links() }}
Related
How can I paginate posts in single category page?
Code
public function show($slug) {
$category = Category::where('slug', $slug)->where('online', true)->with(['posts' => function ($q) {
$q->orderBy('id', 'desc');
//I need this query be paginate
}])->first();
//return....
}
my blade loop
<h1>{{$category->name}}</h1>
#foreach($category->posts as $post)
//post data here
#endforeach
The relationship of categories and posts is n:n;
Because you just need one category, you just need to get posts by one category.
Try to use another method:
public function show($slug) {
$cat = Category::where('slug', $slug)
->where('online', true)
->first();
$posts = Post::join('category_posts', 'category_posts.post_id', '=', 'posts.id')
->where('category_posts.category_id', $cat->id)
->selectRaw('posts.*')
->paginate(10);
//return....
}
And in your blade loop:
<h1>{{$cat->name}}</h1>
#foreach($posts as $post)
//post data here
#endforeach
here is the exact example which I always use in my several project.
Controller:
public function index(Request $request){
$parts = BikeParts::paginate(5); //you can change how many you want
return view('admin.bike_parts.index_excel', compact(['bike_parts']))->with('i', ($request->input('page', 1) - 1) * 5); // put exact number you are paginating , in here I used 5.
}
View:
<table class="table table-actions-bar m-b-0">
<tbody>
<thead>
<tr>
<th>id</th>
<th>PartNo.</th>
<th>Part Name</th>
<th>model</th>
<th>Retail Price</th>
</tr>
</thead>
#foreach($bike_parts as $bike_part)
<tr>
<td>{{$loop->index+1}}</td>
<td>{{$bike_part->part_number}}</td>
<td>{{$bike_part->description}}</td>
<td>{{$bike_part->bike_model}}</td>
<td>{{$bike_part->retail_price}}</td>
</tr>
#endforeach
</tbody>
</table>
{!! $bike_parts->render() !!}
Instead of using first(), use paginate(count)
Here is an example according to your code,
public function show($slug) {
$category = Category::where('slug', $slug)
->where('online', true)
->with(['posts' => function ($q) {
$q->orderBy('id', 'desc');
}])->paginate(10);
//return....
}
$posts = Post::where('category_id', $categoryId)->paginate(10);
Heres a way that may be used to achieve paginated posts in multiple category
However, I suspect that this will make a N+1 query. Might need eager loading implemented with this. Can someone confirm this ?
// Category.php (Model)
public function posts()
{
return $this->hasMany('App\posts', 'post_id', 'id');
}
In the blade file
#foreach($categories as $category)
<h1>{{$category->name}}</h1>
#foreach($category->posts()->paginate() as $post)
//post data here
#endforeach
#endforeach
I want to display data from multiple table to one view, first table is Transaction_in and second table is Transaction_in_detail but beside those two other table are involved.
This is Transcation_in Controller
class Transactions_inController extends Controller
{
public function show($id)
{
$supplierList = Supplier::where('id', 'nama')->first();
$transactionin = Transaction_in::where('id', $id)->first();
$deviceTypeList = DeviceType::where('id', 'nama_tipe_device')->first();
$deviceBrandList = DeviceBrand::where('id', 'nama_brand_device')->first();
$transactionindetail = Transaction_in_detail::where('id', 'Transansaction_in_id')->first();
//return view('transactionsin.show', compact('supplierList', 'transactionsin', 'deviceTypeList', 'deviceBrandList', 'transactionindetail'));
return view('transactionsin.show')->with('transactionsin', $transactionin);
return view('transactionsin.show')->with('transactionsindetail', $transactionindetail);
}
}
Transaction_in Model
class Transaction_in extends Model
{
protected $guarded = [];
public function get_suppliers()
{
return $this->belongsTo(Supplier::class, 'Supplier_id');
}
public function get_devicetypes()
{
return $this->belongsToMany(DeviceType::class, 'DeviceType_id');
}
public function get_devicebrands()
{
return $this->belongsToMany(DeviceBrand::class, 'DeviceBrand_id');
}
public function get_transactionindetail()
{
return $this->belongsToMany(Transaction_in_detail::class, 'Transaction_in_id');
}
}
Transaction_in_detail Model
class Transaction_in_detail extends Model
{
protected $guarded = [];
public function get_transction_in_id()
{
return $this->belongsTo(Transaction_in::class, 'Transaction_in_id');
}
public function get_devicetypes()
{
return $this->belongsToMany(DeviceType::class, 'DeviceType_id');
}
public function get_devicebrands()
{
return $this->belongsToMany(DeviceBrand::class, 'DeviceBrand_id');
}
}
I want to display data from Transaction_in_detail table to Transaction_in Controller, but i have this error
count(): Parameter must be an array or an object that implements
Countable (View:
C:\xampp\htdocs\inventory\resources\views\transactionsin\show.blade.php)
and this is transactionsin.show code https://hastebin.com/ilewesucej.xml
What does your table setup look like, specifically what is the relationship between Transaction_in and Transaction_in_detail? You have a One To Many relationship given in Transaction_in_detail::get_transaction_in_id and a Many to Many relationship given in Transaction_in::get_transactionindetail.
The belongsToMany relationship is for Many to Many relationships with a pivot table. Maybe this is supposed to be hasMany instead?
Start by clarifying that relationship correctly. See the docs on relationships in Laravel. Then you can pull the correct data.
Are you trying to get ONE Transaction_in instance with the id $id? In that case, no, you can't iterate over it. Maybe you're trying for something like this?
$transactionin = Transaction_in::find($id);
$transactionindetail = $transactionin->get_transactionindetail;
// $transactionindetail will now be a `Collection` which implements `Countable`.
Also note that you're (and some of the other answers) are mixing up transactionsin and transactionin (without the 's').
1.
You are using first() method which returns a single object , not array. Instead of returning a collection of models, first() method returns a single model instance. Rewrite your code as following -
$transactionin = Transaction_in::where('id', $id)->get();
For reference, Laravel Docs
2.
You don't have to return view and variables twice. Return once as following -
return view('transactionsin.show',compact('transactionsin','transactionsindetail'));
You don't need to return view two times. use compact for set multiple variable.
public function show($id)
{
$supplierList = Supplier::where('id', 'nama')->first();
$transactionin = Transaction_in::where('id', $id)->first();
$deviceTypeList = DeviceType::where('id', 'nama_tipe_device')->first();
$deviceBrandList = DeviceBrand::where('id', 'nama_brand_device')->first();
$transactionindetail = Transaction_in_detail::where('id', 'Transansaction_in_id')->first();
//return view('transactionsin.show', compact('supplierList', 'transactionsin', 'deviceTypeList', 'deviceBrandList', 'transactionindetail'));
return view('transactionsin.show',compact('transactionsin','transactionsindetail'));
}
In blade file check with empty condition.
#if (!empty($transactionsin))
<div class="tale-responsive">
<table class="table table-hover table-bordered">
<thead align="center">
<tr class="table-primary">
<th>Tipe Perangkat</th>
<th>Brand Perangkat</th>
<th>Spesifikasi</th>
<th>Jumlah</th>
<th>Harga</th>
<th>Total Harga</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ $transactionin->get_devicetypes->nama_tipe_device }}</td>
<td>{{ $transactionin->get_devicebrands->nama_brand_device }}</td>
<td>{{ $transactionin->get_transactionindetail->spek_device }}</td>
<td>{{ $transactionin->get_transactionindetail->harga_device }}</td>
<td>{{ $transactionin->get_transactionindetail->jumlah_device }}</td>
<td>{{ $transactionin->get_transactionindetail->total_harga_device }}</td>
</tr>
</tbody>
</table>
</div>
#else
<h6>No Data Found</h6>
#endif
if you want to make object countable you have to use $object->count()in your case $transactionin->count()
I have the following tables :
orders : id, etc...
order_lines : id, order_id, product_id, etc...
products : id, name, etc...
Foreign keys are defined.
My Laravel Models are defined as :
class Order
public function orderLine()
{
return $this->hasMany('App\OrderLine');
}
class OrderLine
public function order()
{
return $this->belongsTo('App\Order');
}
public function product()
{
return $this->belongsTo('App\Product');
}
class Product
public function orderLine()
{
return $this->hasMany('App\OrderLine');
}
I've tried many things, but nothing is working. Here is the best solution for me, but it's not working.
class OrderController
public function show($id)
{
$user = Auth::user();
$order = Order::where('user_id', '=', $user->id)->with(['orderLine.product'])->findOrFail($id);
return view('layouts/order/index', compact('order'));
}
I struggle to display the following data in the view :
#foreach($order->orderLine as $key => $orderLine)
<tr>
<td>{{$orderLine->product->name}}</td>
<tr>
#endforeach
Product object is not loaded. I want to display the product name in the above loop.
Try to do like this:
public function show($id)
{
$user = Auth::user();
$order = Order::with(['orderLines', 'orderLines.product'])
->where('user_id', '=', $user->id)
->findOrFail($id);
return view('layouts/order/index', compact('order'));
}
class OrderLine
public function order()
{
return $this->belongsTo(\App\Order::class, 'order_id');
}
public function product()
{
return $this->belongsTo(\App\Product::class, 'product_id');
}
class Order
public function orderLines()
{
return $this->hasMany(\App\OrderLine::class);
}
Change name of orderLine to orderLines because order has many orderLines.
And in your blade:
#foreach($order->orderLines as $orderLine)
<tr>
<td>{{$orderLine['product']->title}}</td>
<tr>
#endforeach
Hello Dezaley and welcome to StackOverflow!
Let's investigate your problem.
As far as I can see you are selecting the model in the wrong way. Let me help you:
$order = Order::where(['id' => $id, 'user_id' => $user->id])->with('orderLine.product')->firstOrFail();
The answer from mare96, who was very friendly to help me, is working. However, I found out someting.
You can implement as (mare96 solution)
public function show($id)
{
$user = Auth::user();
$order = Order::with(['orderLines', 'orderLines.product'])
->where('user_id', '=', $user->id)
->findOrFail($id);
return view('layouts/order/index', compact('order'));
}
#foreach($order->orderLines as $orderLine)
<tr>
<td>{{$orderLine['product']->title}}</td>
<tr>
#endforeach
In the view, I don't like the array syntax "$orderLine['product']->title". The following solution without the array is also working.
Solution is below :
public function show($id)
{
$user = Auth::user();
$order = Order::with('orderLines', 'orderLines.product')
->where('user_id', '=', $user->id)
->findOrFail($id);
return view('layouts/order/index', compact('order'));
}
#foreach($order->orderLines as $orderLine)
<tr>
<td>{{$orderLine->product->title}}</td>
<tr>
#endforeach
In fact, my issue was that product was defined to null in the model, so Product was always null in the view.
Class OrderLine extends Model {
public $product = null
I remove line "public $product = null" and it's working as expected. Thanks to the people who helped me.
give it a try -
#foreach($order as $ordr)
<tr>
<td>{{$ordr->product_id->name}}</td>
<tr>
#endforeach
So I keep getting this error when trying to get the name of the user who created a post.
My models look like this:
class User extends Authenticatable {
use Notifiable, CanResetPassword;
public function posts() {
return $this->hasMany('App\Post');
}
}
&
class Post extends Model {
public function user() {
return $this->belongsTo('App\User', 'user_id');
}
}
I try to display the name by doing this in my view:
#foreach($posts as $post)
<tr>
<td>{{ $post->user->name }}</td>
</tr>
#endforeach
And my controller looks like this:
public function getAdminIndex() {
$posts = Post::orderBy('id', 'desc')->get();
$comments = Comment::all();
$likes = Like::all();
$users = User::all();
return view('admin.index', ['posts' => $posts, 'comments' => $comments, 'likes' => $likes, 'users' => $users]);
}
Can anyone please tell me what I'm doing wrong?
Thank you very much!
It means not all posts have user, so do something like this:
<td>{{ optional($post->user)->name }}</td>
Or:
<td>{{ empty($post->user) ? 'No user' : $post->user->name }}</td>
I need to count how many reviews a post has. How would I go about doing that?
Here is my Listing.php Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Listing extends Model
{
public function reviews()
{
return $this->hasMany('\App\Review');
}
}
Here is my Review.php model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Review extends Model
{
protected $fillable = ['stars','name','comment'];
public function listing()
{
return $this->belongsTo('\App\Listing');
}
}
Here is my method that I'am trying to count in controller
public function mostReviews(Request $request) {
$listings = Review::orderBy('-- most reviews here --')->take(10)->get();
$headline = 'Most Reviewed Listings';
return view('pages.listings', compact('listings', 'headline'));
}
Here is my review table:
I haven't tested it but the query below (using the query builder) should give you what you want - 10 listings with the most reviews. Additionally average_stars column should return average stars rate. You can easily modify the query to get 10 highest rated listings by changing orderBy to average_stars.
$listings = \DB::table('reviews AS r')
->select([
'r.listing_id',
\DB::raw('COUNT(*) AS no_of_reviews'),
\DB::raw('AVG(r.stars) AS average_stars'),
'l.*'])
->join('listings AS l', 'l.id', '=', 'r.listing_id')
->limit(10)
->orderBy('no_of_reviews', 'DESC')
->groupBy('listing_id')
->get();
Please note version up to version 5.2 of Laravel this will return array of stdObject. You can easily access those in your blade template in a similar way as Eloquent Collection.
#foreach($listings as $listing)
<tr>
<td>{{ $listing->id }}</td>
<td>{{ $listing->title }}</td>
<td>{{ $listing->no_of_reviews }}</td>
<td>{{ floor($listing->average_stars) }}</td>
</tr>
#endforeach
This is what I did:
public function mostReviews() {
$reviews = DB::table('reviews')
->select(DB::raw('AVG(stars) as review_score, listing_id'))
->groupBy('listing_id')
->orderBy('review_score', 'desc')
->limit(10)
->get();
foreach($reviews as $key => $review) {
$reviews[$key] = Listing::find($review->listing_id);
}
return view('listings.most-reviews', [
'listings' => $reviews
]);
}
Try this
$review = Review::orderBy('-- most reviews here --');
$reviewCount = $review->count();
$listings=$review->take(10)->get();
The following should work
$listing = Listing::with('reviews')->orderByRaw(function($listing)
{
return $listing->review->count();
}, desc)->take(10)->get();
in your listing model:
public function countReview() {
return count($this->reviews);
}
in your views :
{{$listing->countReview()}}
maybe in controller you can write somthing like :
public function mostReviews() {
Review::orderBy('listing', 'desc')->blahblah; // or 'asc'
}