AJAX pagination in Laravel 7 (Internal Server Error) - php

I am using Laravel 7 and I want to use ajax for my pagination so that it would not refresh the whole page when I click for the next page. I searched up to solve my problem but it was not working and it would not go to the fetch() whenever I error_log() in controller. Whenever I click on page 2 it would error. In the console the error shown is:
app.js:16437 GET http://127.0.0.1:8000/ledger/fetch?page=2 500 (Internal Server Error)
In the storage/logs/laravel.log
[previous exception] [object] (BadMethodCallException(code: 0): Method Illuminate\\Database\\Eloquent\\Collection::links does not exist. at C:\\xampp\\htdocs\\final_Financial\\fin_book_09\\vendor\\laravel\\framework\\src\\Illuminate\\Support\\Traits\\Macroable.php:103)
LedgerController.php
public function index(Request $request)
{
$disableLedger = true;
$ledger = Ledger::orderBy('id', 'DESC')
->where('user_id', auth()->user()->id)
->paginate(5);
$ledgerCatType = DB::table('ledgers')
->orderBy('ledgers.id', 'DESC')
->join('categories', 'ledgers.cat_id', '=', 'categories.id')
->where('ledgers.user_id', auth()->user()->id)
->get('categories.type');
$category = Category::where('user_id', auth()->user()->id)->get();
return view('ledgers.index', [
'ledgers' => $ledger,
'categories' => $category,
'disableLedger' => $disableLedger,
'ledgerCatType' => $ledgerCatType
]);
}
function fetch(Request $request)
{
if ($request->ajax())
{
$ledger = Ledger::orderBy('id', 'DESC')
->where('user_id', auth()->user()->id)
->paginate(5);
$ledgerCatType = DB::table('ledgers')
->orderBy('ledgers.id', 'DESC')
->join('categories', 'ledgers.cat_id', '=', 'categories.id')
->where('ledgers.user_id', auth()->user()->id)
->get('categories.type');
$category = Category::where('user_id', auth()->user()->id)->get();
return view('ledger.entries', compact('ledger', 'categories', 'ledgerCatType'))->render();
}
}
script
$(document).ready(function () {
$('.pagination a').on('click', function(event) {
event.preventDefault();
var page = $(this).attr('href').split('page=')[1];
fetch(page);
})
function fetch(page) {
$.ajax({
url : '/ledger/fetch?page='+page,
success : function(data)
{
$('#ledger-entry').html(data);
},
error : function(){
alert("error!!!!");
}
});
}
});
under the folder ledgers which will be called in the index.blade.php
entries.blade.php
<div class="outer-cont">
<table class="table container inner-cont">
<thead>
<th scope="col">Date</th>
<th scope="col">Description</th>
<th scope="col">Category</th>
<th scope="col">Amount</th>
<th scope="col"></th>
</thead>
<tbody>
#for($i = 0; $i < count($ledgers); $i++)
<tr scope="row">
<td class="tbl-date">{{ $ledgers[$i]->month }} {{ $ledgers[$i]->day }}, {{ $ledgers[$i]->year }}</td>
<td class="tbl-desc">{{ $ledgers[$i]->description }} </td>
<td>{{ $ledgers[$i]->category}} </td>
#if($ledgerCatType[$i]->type == "Expense")
<td class="tbl-amount" style="color: #FF5349;">Php ({{ $ledgers[$i]->amount }})</td>
#else
<td class="tbl-amount" style="color: #3BC23E;">Php {{ $ledgers[$i]->amount }}</td>
#endif
</tr>
#endfor
</table>
</div>
<div class="pagination">{!! $ledgers->links() !!}</div>
index.blade.php
<div id="ledger-entry">
#include('ledgers.entries')
</div>
web.php
Route::resource('ledger','LedgerController')->middleware('auth');
Route::get('/ledger/fetch', 'LedgerController#fetch')->middleware('auth');

Okay nevermind, I fixed it. I'm just going to post the answer here in case anyone gets stuck with it too.
The problem was my route, instead of using the resource I manually added the routes:
Route::get('ledger', 'LedgerController#index')->name('ledger.index')->middleware('auth');
Route::post('/ledger', 'LedgerController#store')->name('ledger.store')->middleware('auth');
Route::delete('/ledger/{id}', 'LedgerController#destroy')->name('ledger.destroy')->middleware('auth');
Route::get('/ledger/fetch', 'LedgerController#index')->name('ledger.fetch')->middleware('auth');
I also edited my code for the index() and thus combining it with the fetch() method thus it now looks like this:
public function index(Request $request)
{
$disableLedger = true;
$ledgers = Ledger::orderBy('id', 'DESC')
->where('user_id', auth()->user()->id)
->paginate(10);
$ledgerCatType = DB::table('ledgers')
->orderBy('ledgers.id', 'DESC')
->join('categories', 'ledgers.cat_id', '=', 'categories.id')
->where('ledgers.user_id', auth()->user()->id)
->select('categories.type')
->paginate(10);
$categories = Category::where('user_id', auth()->user()->id)->get();
if ($request->ajax())
{
return view('ledgers.entries', compact('ledgers', 'categories', 'ledgerCatType', 'disableLedger'))->render();
}
return view('ledgers.index', compact('ledgers', 'categories', 'ledgerCatType', 'disableLedger'));
}

Related

Destroy() in Basic Controller is not working

So I'm printing user complaints in table where I'm also printing a Delete button with every row. When I click that delete button, I want to delete that specific complaint from the table. I'm not using Resource Controller for this but a Basic Controller. Now, this is my code:
ViewComplaint.blade.php (Complaints Table with Delete Button):
<table id="cTable" class="table table-striped table-bordered">
<thead>
<tr>
<th>Student Name</th>
<th>Complaint Title</th>
<th>Complaint Description</th>
<th>Action</th>
</tr>
</thead>
<tbody>
#foreach($complaints as $complaint)
<tr>
<td>{{ $complaint->name }}</td>
<td>{{ $complaint->cname }}</td>
<td>{{ $complaint->cbody }}</td>
<td class="btn-group">
{!! Form::open(array('route'=>['complaint.destroy',$complaint->id],'method'=>'DELETE')) !!}
{!! Form::submit('Delete',['type'=>'submit','style'=>'border-radius: 0px;','class'=>'btn btn-danger btn-sm',$complaint->id]) !!}
{!! Form::close() !!}
</td>
</tr>
#endforeach
</tbody>
</table>
Web.php (Routes):
Route::get('/complaint/create','ComplaintController#create')->name('complaint.create');
Route::post('/complaint','ComplaintController#store')->name('complaint.store');
Route::get('/complaint','ComplaintController#index')->name('complaint.index');
Route::delete('/complaint/{$complaint->id}','ComplaintController#destroy')->name('complaint.destroy');
ComplaintController.php (Basic Controller):
class ComplaintController extends Controller
{
public function index() {
$complaints = Complaint::all();
return view('viewcomplaint',compact('complaints'));
}
public function create(User $user) {
$user = User::all();
$user->name = Auth::user()->name;
return view('createcomplaint',compact('user'));
}
public function store(Request $request, Complaint $complaint, User $user) {
$user = User::find($user);
$complaint->name = Auth::user()->name;
$complaint->cname = $request->input('cname');
$complaint->cbody = $request->input('cbody');
//update whichever fields you need to be updated
$complaint->save();
return redirect()->route('home.index');
}
public function destroy(Complaint $complaint,$id)
{
$complaint = Complaint::findOrFail($complaint->id);
$complaint->delete();
return redirect()->route('complaint.index');
}
}
Now when I click the Delete button on the table, it just gives me "404 | Not Found" error. What am I doing wrong here? I would really appreciate some help.
remove the $id from the route
Route::delete('/complain/{id}','ComplaintController#destroy')->name('complaint.destroy');
public function destroy($id) {
}
The route parameter is just a name; you are saying this particular route segment is dynamic and I want the parameter named complaint:
Route::delete('complaint/{complaint}', 'ComplaintController#destroy')->name('complaint.destroy');
Then you can adjust your destroy method to take the parameter complaint typehinted as Complaint $complaint to get the implicit binding:
public function destroy(Complaint $complaint)
{
$complaint->delete();
return redirect()->route('complaint.index');
}
Seems to me you're defining your route wrong. Change your route to:
Route::delete('/complaint/{id}','ComplaintController#destroy')->name('complaint.destroy');
You don't need an array() in your form opening, so hange your form opening to this:
{!! Form::open(['method' => 'DELETE', 'route' => ['complaint.destroy',$complaint->id]]) !!}
And remove the $complaint->id from your submit button, you don't need it there.
All you have to do now inside your function is to find Complaint that has the id you passed in your form:
public function destroy($id)
{
$complaint = Complaint::findOrFail($id);
$complaint->delete();
return redirect()->route('complaint.index');
}
Let me know if you stumble on any errors.

Laravel - How to export Excel data with Eloqeunt relationship from view?

I'm currently using "maatwebsite/excel": "3.1.10", recently switched from 2.x version, and I'm having a problem with displaying excel data from view that is using Eloquent relationship. Here's all my code below.
UsersEarningHistory.php:
<?php
namespace App\Exports;
use App\EarningHistory;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Events\AfterSheet;
use Maatwebsite\Excel\Concerns\WithMapping;
class UsersEarningHistory implements FromView, ShouldAutoSize, WithEvents, WithMapping
{
protected $history;
public function __construct($history = null)
{
$this->history = $history;
}
public function view(): View
{
return view('admin.commission.excel_table', [
'history' => $this->history ?: EarningHistory::all()
]);
}
public function registerEvents(): array
{
return [
AfterSheet::class => function(AfterSheet $event) {
$cellRange = 'A1:W1'; // All headers
$event->sheet->getDelegate()->getStyle($cellRange)->getFont()->setSize(14)->setBold($cellRange);
},
];
}
}
UserController where my method for exporting data is:
public function index(Request $request)
{
//
$active = $this->active;
$active[1] = 'commission';
view()->share('active', $active);
$breadcrumbs = [
['link' => '/admin', 'text' => 'Administration'],
['text' => 'Commission']
];
view()->share('breadcrumbs', $breadcrumbs);
$styles[] = '//cdn.jsdelivr.net/bootstrap.daterangepicker/2/daterangepicker.css';
view()->share('styles', $styles);
$scripts[] = '//cdn.jsdelivr.net/momentjs/latest/moment.min.js';
$scripts[] = '//cdn.jsdelivr.net/bootstrap.daterangepicker/2/daterangepicker.js';
$scripts[] = '/js/admin/commission.js';
view()->share('scripts', $scripts);
$history = new EarningHistory();
if ($request->filled('q')) {
$history = $history->whereHas('user', function ($query) use ($request) {
$query->where('username', 'like', '%' . $request->q . '%');
});
}
if ($request->filled('qn')) {
$history = $history->whereHas('user', function ($query) use ($request) {
$query->where('first_name', 'like', '%' . $request->qn . '%');
$query->orWhere('last_name', 'like', '%' . $request->qn . '%');
if (strpos( $request->qn, ' ') !== false) { // is both
$both = explode(" ",$request->qn);
if(isset($both[0]))
$query->orWhere('first_name', 'like', '%' . $both[0] . '%');
if(isset($both[1]))
$query->orWhere('last_name', 'like', '%' . $both[1] . '%');
}
});
}
if($request->filled('has_correct_ratio')) {
$history = $history->where('has_correct_ratio', $request->filled_correct_ratio);
}
if (!$request->filled('null')) {
$history = $history->where(function ($query) {
$query->where('personal_balance', '!=', 0)
->orWhere('group_balance', '!=', 0);
});
}
if ($request->filled('date')) {
$history = $history->whereBetween('created_at', [Carbon::parse('01.' . $request->date)->firstOfMonth(),
Carbon::parse('01.' . $request->date)->addMonth()->firstOfMonth()]);
}
if ($request->filled('export')) {
$date = $request->filled('date') ? 'Earning history for ' . Carbon::parse('01.' . $request->date)->format('F') :
'Earning history for all time';
return Excel::download( new UsersEarningHistory($history), $date.'history.xls');
}
$data['history'] = $history->paginate(15);
$data['request'] = $request;
return view('admin.commission.index', $data);
}
Export table blade:
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Name</th>
<th>Month</th>
<th>Amount</th>
<th>World Bonus</th>
<th>Total amount</th>
<th>Personal points</th>
<th>Group points</th>
{{--<td></td>--}}
</tr>
</thead>
<tbody>
#foreach($history as $obj)
<tr>
<td>
{{ $obj->user->first_name . ' ' . $obj->user->last_name }}
</td>
<td>{{$obj->created_at->format('F')}}</td>
<td>€{{ number_format($obj->personal_balance + $obj->group_balance, 2)}}</td>
<td>€{{ number_format($obj->world_bonus, 2)}}</td>
<td>€{{ number_format($obj->personal_balance + $obj->group_balance + $obj->world_bonus, 2)}}</td>
<td>
{{ intval($obj->personal_points) }}
</td>
<td>
{{ intval($obj->group_points) }}
</td>
<td>
{{ App\User::$_RANK[$obj->rank]['title'] }}
</td>
{{--<td align="center">--}}
{{--<a href="/admin/payouts/{{$obj->id}}" class="btn btn-primary btn-xs">--}}
{{--<i class="icon-eye"></i>--}}
{{--</a>--}}
{{--<!--TODO Prikaži akciju na osnovu trenutnog statusa-->--}}
{{--#if($obj->status=='pending')--}}
{{--<a href="javascript:;" class="ajax-action btn btn-sm btn-success" data-action="/ajax/payout-change-status" --}}
{{--data-obj-id="{{$obj->id}}" data-status="approved">#lang('form.approve')</a>--}}
{{--<a href="/admin/payouts/reject/{{$obj->id}}" class="btn btn-sm btn-danger" >#lang('form.reject')</a>--}}
{{--#endif--}}
{{--</td>--}}
</tr>
#endforeach
</tbody>
</table>
As you can see, I'm trying to get user name from his earning history, but when I try to export data, my excel file is empty, but it's not giving me any errors.
Note: EarningHistory is related with User model:
//EarningHistory model
public function user()
{
return $this->belongsTo('App\User', 'user_id', 'id')->withTrashed();
}
//User model
public function earning_history()
{
return $this->hasMany('App\EarningHistory');
}
Found a solution. My $history variable in UserController was returning Query builder, because I forgot to add ->get(); method :
return Excel::download( new UsersEarningHistory($history->get()), $date.'history.xls');
Now everything works as it should.

Custom Calculation in Laravel Controller

I am new to Laravel. I'm building a small app which displays the user table from the DB, but before sending it to the view, I want to include a custom value from another function.
The accounts table fetches the list of accounts from MySQL, and I want to include a custom function called getStatus() which I get from an API.
Code
<?php
public function accounts()
{
$accounts = DB::table('accounts')
->where('profile_id', '=', Auth::id())
->get();
foreach ($accounts as $account) {
$account->status = getStatus($account->accno);
}
$data = compact('accounts');
return view::make('transactions', ['accounts' => $data]);
}
View
#foreach ($accounts as $account)
<tr>
<td></td>
<td>{{ $account->login }}</td>
<td>{{ $account->status }}</td>
</tr>
#endforeach
You can do it like this.
$accounts = $accounts->map(function($account){
$account->status = getStatus($account->accno)
});
Hope this will help you.
Thanks
$accounts = DB::table('accounts')
->where('profile_id', '=', Auth::id())
->get()
->map(function($item){
$item->status = getStatus($item->accno);
return $item;
});
Now you'll have status in your $accounts.

updateExistingPivot won't work

I'm trying to update my pivot table approve_document where it has a extra column isApprove using ->withPivot method.
Model:
Document
class Document extends Model
{
public function sentToApprovers()
{
return $this->belongsToMany('App\Models\Approve', 'approve_document')->withPivot('isApprove');
}
}
Approve
class Approve extends Model
{
public function createdpendingDocuments()
{
return $this->belongsToMany('App\Models\Document', 'approve_document')->withPivot('isApprove');
}
}
This is where I get all my records in my approve_document.
Controller:
public function documentsSentForApproval()
{
$pendingDocumentLists = DB::table('approve_document')
->select('documents.title', 'documents.content', 'categories.category_type', 'users.username', 'approve_document.dateReceived', 'documents.id', 'approves.approver_id')
->join('documents', 'documents.id', '=', 'approve_document.document_id')
->join('categories', 'categories.id', '=', 'documents.category_id')
->join('approves', 'approves.id', '=', 'approve_document.approve_id')
->join('users', 'users.id', '=', 'approves.approver_id')
->where('approver_id', '=', Auth::id())
->orWhere('requestedBy', '=', Auth::id())
->get();
return view ('document.pending')
->with('pendingDocumentLists', $pendingDocumentLists);
}
View:
#foreach ($pendingDocumentLists as $list)
<tr class = "info">
<td>{{ $list->title }}</td>
<td>{{ strip_tags(substr($list->content, 0, 50)) }} {{ strlen($list->content) > 50 ? "..." : '' }}</td>
<td>{{ $list->category_type }}</td>
<td>{{ $list->username }}</td>
<td>{{ date('M, j, Y', strtotime($list->dateReceived)) }}</td>
<td>
#if (Auth::id() == $list->approver_id)
<a href = "{{ route ('document.pending', $list->id) }}">
<button type = "submit" class = "btn btn-success glyphicon glyphicon-thumbs-up"> Approve</button>
</a>
#endif
</td>
<td></td>
</tr>
#endforeach
You can see here I have a approve button where I need to set isApprove to true when the button is clicked. You can see that I get the current id of the document when the button was clicked.
This part of the Controller where I'm having a hard time updating my pivot table. It gives me a error MethodNotAllowedHttpException. Any tips or help would greatly appreciated!
public function updateIsApprove($id)
{
$document = new Document();
foreach ($document as $update)
{
$approve = new Approve();
$document->sentToApprovers()->updateExistingPivot([$approve->id => ['isApprove' => '1']],false);
}
return redirect()->route('document.pending');
}
routes:
Route::post('/documents/pending/approve/{id}',
[
'uses' => '\App\Http\Controllers\DocumentController#updateIsApprove',
'as' => 'document.pending',
]);
public function updateExistingPivot($id, array $attributes, $touch = true)
First parametr should be id of related thing.
public function updateIsApprove($documentId, $approveId)
{
$document = Document::find($documentId);
if (!$document) {
// Handle error that document not exists.
}
$approve = $document->sentToApprovers()->find($approveId);
if (!$approve) {
// Handle that approve not exists or is not related with document.
}
$document->sentToApproves()->updateExistingPivot($approve->id, ['isApprove' => 1]);
return redirect()->route('document.pending');
}
MethodNotAllowedHttpException is not for your controller but is for your Route. As you can see, in your Routes file, you have Route for handling POST request, but in your view you are making a GET request by accessing the URL directly.
So, just change the POST route to GET in your Routes file.
Route::get('/documents/pending/approve/{id}',
[
'uses' => '\App\Http\Controllers\DocumentController#updateIsApprove',
'as' => 'document.pending',
]);

Laravel some questions (pagination, if - foreach)

I'm laravel newbie. I'm created simple code and I have some questions:
I think this code bad (it works, but I use #forelse($forums as $forum) and anywhere use $forum)
#extends('layouts.main')
#section('content')
#forelse($forums as $forum) <-- I don't like this line, it works but i think it's possible with if or something else
#forelse($topics as $topic)
{{ $topic->title }}<br>
#empty
Sorry but this forums empty.
#endforelse
#empty
Sorry but this forum not found
#endforelse
#stop
And second question how to make pagination? I'm tried this:
<?php
namespace App\Http\Controllers;
use DB;
use View;
class viewForum extends Controller
{
public function showForum($fname, $fid)
{
return View::make('forum', [
'forums' => DB::table('forums')
->where('id', $fid)
->where('seo_name', $fname)
->select()
->get()
->simplePagination(5)
]);
}
}
But not work's, I'm tried tutorials..etc, how to? Thanks so much in advance ! :)
for your first question. You can use #foreach or #each. these are the two that i usually used.
for your second question:
return View::make('forum', [
'forums' => DB::table('forums')
->where('id', $fid)
->where('seo_name', $fname)
->select()
->paginate(5);
]);
remove ->get()
and replace simplePagination(5) with paginate(5)
documation http://laravel.com/docs/5.0/pagination
Update
change you code block from
return View::make('forum', [
'forums' => DB::table('forums')
->where('id', $fid)
->where('seo_name', $fname)
->select()
->paginate(5);
]);
to
$forums = DB::table('forums')
->where('id', $fid)
->where('seo_name', $fname)
->select()
->paginate(5);
return View::make('forum', compact('forums'));
then check if $forums->render() got error.
Update
$forums = DB::table('forums')
->where('id', $fid)
->where('seo_name', $fname)
->select()
->get(5);
$topics = DB::table('topics')
->where('forum_id', $id)
->select()
->paginate(2)
return View::make('forums', compact('forums', 'topics'));
on your view you do <?php echo $topics->render() ?> since topic is the one you paginate. also you can remove ->select() from your code. if you don't specify fields to output.
For #foreach ($topics as $topic)
{{$topic->title}}
#endforeach
For Pagination
$users = User::where('status','1')
->paginate(10);
Note: In View add this {{$user->links()}} for getting the pagination links.
you can use #foreach() #endforeach and probably #if #else #endif
see sample:
#foreach($forums as $forum)
#if($forum==0)
{{'empty'}}
#else
{{ 'Not empty' }}
#endif
#endforeach
for pagination i will suggest you use jquery datatable for proper pagination. Its quite okay and saves lots of time. see below the sample implementation:
//this preload the jquery library for datatable together with the print button
<script src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/buttons/1.5.2/js/dataTables.buttons.min.js"></script>
<script src="https://cdn.datatables.net/buttons/1.5.2/js/buttons.print.min.js"></script>
//this section call the document ready event making sure that datatable is loaded
<script>
$(document).ready(function() {
$('#').DataTable();
} );
//this section display the datatable
$(document).ready(function() {
$('#mytable').DataTable( {
dom: 'Bfrtip',
"pageLength": 5, //here you can set the page row limit
buttons: [
{
extend: 'print',
customize: function ( win ) {
$(win.document.body)
.css( 'font-size', '10pt' )
.prepend(
''
);
$(win.document.body).find( 'table' )
.addClass( 'compact' )
.css( 'font-size', 'inherit' );
}
}
]
} );
} );
</script>
//you can display record on the datatable as shown below
<div class="table-responsive col-md-12">
<table id="mytable" class="table table-bordered table-striped table-highlight">
<thead>
<tr bgcolor="#c7c7c7">
<th>S/N</th>
<th>Name</th>
</tr>
</thead>
<tbody>
#php
$i=1;
#endphp
#foreach($queryrecord as $list)
<tr>
<td>{{ $i++ }}</td>
<td>{{ $list->name }}</td>
</tr>
#endforeach
</tbody>
</table>
<hr />
</div>
Note: remember before displaying information on the datatable, you must have query your record from database.i'm using query builder here as sample
$data['queryrecord']=DB::table('tablename')->get();

Categories