How to display subnested arrays in Vue search component - Laravel - laravel

I'm having issues displaying eloquent relationship data via my vue.js search template, specifically my customer->biller fields.
models.student_first_name will display however
models.billers.biller_first_name wont show anything
The search function is working on my customers.index view using the following
CustomerDetailsController:
public function getData(Request $request){
$search = $request->search;
$customer = Customer::with('orders', 'billers', 'paymentplans', 'paidinfulls')->where(DB::raw('concat(student_first_name," ",student_last_name)'), 'like', '%'.$search.'%')->paginate(20);
return response()->json([
'model' => $customer
]);
}
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$customers = Customer::with('orders', 'paymentplans', 'billers')->orderBy('created_at', 'desc')->where('owner', Auth::user()->name)->paginate(25);
return view('customers.index', compact('customers', 'orders', 'paymentplans', 'funding'));
}
Customers.vue:
<template>
<div class="container">
<div class="field m-b-20">
<p class="control has-icons-left">
<input #keyup="fetchDataCustomer()" type="text" class="form-control input" name="search" v-model="search" placeholder="Search">
<span class="icon is-small is-left"><i class="fa fa-search"></i></span>
</p>
</div>
<table class="table">
<thead>
<tr>
<th>Student Name</th>
<th>Email</th>
<th>Biller Name</th>
<th>Biller Email</th>
<th>Courses Purchased</th>
<th>Deposit</th>
<th>Payment Plan</th>
<th>Accepted</th>
<th>Receipted</th>
</tr>
</thead>
<tbody>
<tr v-for="models in model.data">
<td>{{models.student_first_name}} {{models.student_last_name}}</td>
<td>{{models.student_email}}</td>
<td>{{models.billers.biller_first_name}}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import axios from 'axios'
export default{
data(){
return {
model: {},
search:'',
url:'api/customers'
}
},
created:function (){
this.fetchDataCustomer()
},
methods: {
fetchDataCustomer(){
var vm = this
axios.get(`${this.url}?search=${this.search}`)
.then(function (response) {
Vue.set(vm.$data, 'model', response.data.model)
})
}
}
}
</script>
JSON output of a search result
{"model":{"current_page":1,"data":[{"id":"627d0130-4dd0-11e8-91dd-69869f509337","enrolment_id":"John","student_first_name":"John","student_last_name":"Smith","student_email":"johnsmith#mail.com","address":"123 Test Street","suburb":"Townsville","postcode":"9999","state":"AAA","home_phone":null,"work_phone":null,"mobile_phone":"0444444444","dob":"1999-01-01 00:00:00","drivers_license_number":"123738974987498","owner":"Sales Man","owner_email":"salesman#business.com","created_at":"2018-05-02 16:16:43","updated_at":"2018-05-02 16:16:43","orders":[{"id":933,"fc_account":16,"customer_id":"627d0130-4dd0-11e8-91dd-69869f509337","biller_id":246,"enrolment_id":"John","course_id":5,"delivery_mode":0,"course_cost":"2000.00","deposit":null,"gov_funding":"0","location":"0","Monday":0,"Tuesday":0,"Wednesday":0,"Thursday":0,"Friday":0,"Saturday":0,"Sunday":0,"start_time":null,"end_time":null,"start_date":null,"enrolment_issue_date":"2018-05-02","sale_type":2,"created_at":"2018-05-02 16:17:24","updated_at":"2018-05-02 16:17:24"}],"billers":[{"id":246,"customer_id":"627d0130-4dd0-11e8-91dd-69869f509337","biller_first_name":"John","biller_last_name":"Smith","biller_email":"johnsmith#mail.com","biller_address":null,"biller_suburb":null,"biller_postcode":null,"biller_state":null,"biller_phone":null,"created_at":"2018-05-02 16:17:24","updated_at":"2018-05-02 16:17:24"}],"paymentplans":[{"id":"836e7e40-4dd0-11e8-a907-83f96c25a7d0","enrolment_id":"John","customer_id":"627d0130-4dd0-11e8-91dd-69869f509337","paysmart_id":"FIT69869f509337","biller_id":246,"biller_first_name":"John","biller_last_name":"Smith","biller_email":"johnsmith#mail.com","payment_method":-1,"contract_value":"1500.00","interest_free":1,"payment_frequency":1,"commencement_date":"2018-05-24 00:00:00","payment":"100.00","first_payment":"200.00","admin_fee":"1.30","setup_fee":"5.50","deposit":"500.00","deposit_payment_method":"Cash","special_conditions":null,"accepted":null,"accepted_student":null,"created_at":"2018-05-02 16:17:38","updated_at":"2018-05-02 16:17:38","submitted":1,"biller_ip":null,"student_ip":null}],"paidinfulls":[]}]
Vue Debug

Your models.billers is an array but you're treating it as an object. So you'll have to access it via models.billers[0].biller_first_name instead of models.billers.biller_first_name given that it is always available.

Related

Laravel 5.8 and datatable form validation

I am using Laravel 5.8 and Yajra datatable package.
I have to build a dynamic form from the controller (it works well).
After submitting the form, when the validation fails, I want to display the old values in the form.
Display the form/datatable and validation work fine. I need the way to display the old values.
Controller (build the form) (just an excerpt as it works fine)
if (request()->ajax())
{
return datatables()
->of($memberships)
->addIndexColumn()
->addColumn('amount', function($data) use ($membership, $amounts) {
$link = '<input type="text" id="membership_id[]" name="membership_id[]" value="' . $data->id . '" style="width:100px" class="form-control">';
$link .= '<input type="number" id="amount[]" name="amount[]" value="">';
return $link;
})
->rawColumns(['amount'])
->make(true);
}
return view('my_blade', compact('memberships', 'id'));
My question here is How to set the value attribute in the amount input field when the validation fails?
my_blade (display datatable form) (just an excerpt as it works fine)
#if (count($errors) > 0)
<div class="alert alert-danger">
<ul>
#foreach ($errors->all() as $error)
<li>{{ $error }}</li>
#endforeach
</ul>
</div>
#endif
<form method="POST" id="my_form" action="{{ route('my_submit_route') }}">
#csrf
<input type="text" id="id" name="id" value="{{ $id }}">
<table id="my_datatable" class="table table-hover table-striped table-responsive">
<thead class="crud-header">
<tr>
<th>No</th>
<th>Name</th>
<th>Description</th>
<th>Status</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div class="form-group row">
<div class="col text-center">
<button type="submit" class="btn btn-primary">
Save
</button>
</div>
</div>
</form>
</div>
</div>
#endsection
#section('script')
<script>
$(document).ready(function() {
/* Start Datatable */
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
$.noConflict();
$('#my_datatable').DataTable({
iDisplayLength: 100,
serverSide: true,
processing: true,
ajax: {
url: "{{ route('my_route', ['id' => $id]) }}",
type: 'GET',
},
columnDefs: [
{className: "dt-center", targets: "_all"}
],
columns: [
{data: 'DT_RowIndex', name: 'DT_RowIndex', orderable: false},
{data: 'name', name: 'name'},
{data: 'given_name', name: 'given_name'},
{data: 'amount', name: 'amount'}
],
order: [[1, 'desc']]
});
/* End Datatable */
});
</script>
#endsection
My validation works fine. Just don't know how to display the old value when validation fails
My Controller (to process form submit)
/* get the array of membership IDs */
$membershipIDs = $request->membership_id;
$validate_array = [];
/* Loop through membership for validation */
for($i = 0; $i < count($membershipIDs); $i++)
{
$validate_array['amount.'. $i] = 'sometimes|nullable|numeric|gte:0';
}
$validator = Validator::make($request->all(), $validate_array);
if ($validator->fails())
{
/* Get the array of amounts */
$amounts = $request->amount;
// How to manage here to return in the form and display the old values and the error message on the failed fields?
}
else
{
//Process the form;
}
My question: How to manage in the controller to return in the form and display the old values and the error messages on the failed fields?
I also use the following for validation, the validation works well but I cannot display the old values in the form after the validation fails.
$this->validate($request, $validate_array);
Thanks
Try this in your controller:
if ($validator->fails()) {
return back()->withInput()->withErrors($validator);
}
Not sure if this also works in Laravel 5. But an alternate to this is:
if ($validator->fails()) {
return redirect('post/create')
->withErrors($validator)
->withInput();
}
Which works in Laravel 5

404 error on route that exists in Laravel 8

I get a 404 error when I access this edit_product route that is served by a Product controller, codes are attached below
Route::get('/edit_product{id}', 'ProductController#editproduct');
I would like your help in finding where the error in my code is. Thank you.
Here is the relevant code:
web.php
<?php
use Illuminate\Support\Facades\Route;
Route::get('/', 'ClientController#home');
Route::get('/checkout', 'ClientController#checkout');
Route::get('/shop', 'ClientController#shop');
Route::get('/cart', 'ClientController#cart');
Route::get('/login', 'ClientController#login');
Route::get('/signup', 'ClientController#signup');
Route::get('/dashboard', 'AdminController#dashboard');
Route::get('/addcategories', 'CategoryController#addcategories');
Route::post('/savecategories', 'CategoryController#savecategories');
Route::get('/categories', 'CategoryController#categories');
Route::get('/edit_category{id}', 'CategoryController#edit');
Route::get('/delete{id}', 'CategoryController#delete');
Route::get('/addslider', 'SliderController#addslider');
Route::get('/sliders', 'SliderController#slider');
Route::get('/addproducts', 'ProductController#addproducts');
Route::get('/products', 'ProductController#products');
Route::post('/saveproducts', 'ProductController#saveproduct');
Route::get('/edit_product{id}', 'ProductController#editproduct');
Route::get('/orders', 'ProductController#orders');
products.blade.php
#extends('admin.layouts.appadmin')
#section('title')
Products
#endsection
#section('content')
{{Form::hidden('', $increment=1)}}
<div class="card">
<div class="card-body">
<h4 class="card-title">Products table</h4>
<div class="row">
<div class="col-12">
<div class="table-responsive">
<table id="order-listing" class="table">
<thead>
<tr>
<th>Order #</th>
<th>Image</th>
<th>Name</th>
<th>Price</th>
<th>Category</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
#foreach ($products as $product)
<tbody>
<tr>
<td>{{$increment}}</td>
<td><img src="/storage/product_images/{{$product->product_image}}"></td>
<td>{{$product->product_name}}</td>
<td>KES{{$product->product_price}}</td>
<td>{{$product->product_category}}</td>
#if($product->status ==1)
<td>
<label class="badge badge-success">Activated</label>
</td>
#else
<td>
<label class="badge badge-danger">Un Activated</label>
</td>
#endif
<td>
<button class="btn-sm btn-outline-info" onclick="window.location ='{{url('/edit_product/'.$product->id)}}'">Edit</button>
Delete
#if($product->status==1)
Unactivate
#else
Activate
#endif
</td>
</tr>
{{Form::hidden('', $increment=$increment+1)}}
#endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
#endsection
#section('scripts')
<script src="{{'backend/js/data-table.js'}}"></script>
#endsection
ProductController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Product;
use App\Models\Category;
class ProductController extends Controller
{
public function editproduct($id){
$product=Product::find($id);
return view('admin.product')->with('product', $product);
}
public function products(){
$products=Product::get();
return view('admin.products')->with('products', $products);
}
public function orders(){
return view('admin.orders');
}
public function addproducts(){
$categories=Category::All()->pluck('category_name', 'category_name');
return view('admin.addproducts')->with('categories', $categories);
}
public function saveproduct(Request $request){
$this->validate($request, ['product_name'=> 'required',
'product_price'=> 'required',
'product_image'=>'image|nullable|max:1999']);
if($request->input('product_category')){
if($request->hasFile('product_image')){
//1 : get filename with ext
$fileNameWithExt = $request->file('product_image')->getClientOriginalName();
//2 : get just file name
$fileName = pathinfo($fileNameWithExt, PATHINFO_FILENAME);
//3 : get just extension
$extension = $request->file('product_image')->getClientOriginalExtension();
//4 : file name to store
$fileNameToStore = $fileName.'_'.time().'.'.$extension;
//upload image
$path =$request->file('product_image')->storeAs('public/product_images', $fileNameToStore);
}
else{
$fileNameToStore ='noimage.jpg';
}
$product=new Product();
$product->product_name =$request->input('product_name');
$product->product_price =$request->input('product_price');
$product->product_category =$request->input('product_category');
$product->product_image =$fileNameToStore;
$product->status =1;
$product->save();
return redirect('/addproducts')->with('status', 'The '.$product->product_name.' Product has been saved successfully');
}else{
return redirect('/addproducts')->with('status1', 'You need to select a Category for your product');
}
}
}
I have tried.
replacing it with full namespace:
Route::get('/edit_product{id}', 'App\Http\Controllers\ProductController#editproduct');
php artisan cache:clear
php artisan cache:cache
Change your route to:
Route::get('/edit_product/{id}', 'ProductController#editproduct')
and also your delete and edit_category route:
Route::get('/delete/{id}', 'CategoryController#delete');
Route::get('/edit_category/{id}', 'CategoryController#edit');
you forgot the / in this route:
Route::get('/edit_product{id}', 'ProductController#editproduct');
change it to
Route::get('/edit_product/{id}', 'ProductController#editproduct');
and clear the route cache:
php artisan route:cache

Checkbox doesn't save true value

I'm using Laravel 5.6 and Vuejs 2.
If I click on my checkbox and make the value true it's supposed to save a 1 in the database and if I click my checkbox and make the value false it saves a 0.
The problem I'm having is that if I click my checkbox and make it true, it doesn't save the correct value, no changes is made to the database and I don't get any errors. If I click on my checkbox and make it false, it saves the 0 correctly.
I did notice that even when my value is supposed to be true, I do get a false when I dd($category->has('active')
I'm not sure where I'm going wrong or how to fix it.
My vue file
<template>
<div class="card-body">
<table class="table">
<thead class="thead-dark">
<tr>
<th scope="col">Active</th>
<th scope="col">Title</th>
<th scope="col">Edit</th>
<th scope="col">Delete</th>
</tr>
</thead>
<tbody>
<tr v-for="(category, index) in categoriesNew" >
<td>
<label>checkbox 1</label>
<input name="active" type="checkbox" v-model="category.active" #click="checkboxToggle(category.id)">
</td>
<td>
{{ category.title }}
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
props: ['categories'],
data(){
return {
categoriesNew: this.categories
}
},
methods: {
checkboxToggle(id){
console.log(id);
axios.put('/admin/category/active/'+id, {
categories: this.categoriesNew
}).then((response) => {
//Create success flash message
})
},
},
mounted() {
console.log('Component mounted.')
}
}
</script>
my routes
Route::put('admin/products/updateAll', 'Admin\ProductsController#updateAll')->name('admin.products.updateAll');
Route::put('admin/category/active/{id}', 'Admin\CategoryController#makeActive')->name('admin.category.active');
Route::resource('admin/category', 'Admin\CategoryController');
Route::resource('admin/products', 'Admin\ProductsController');
my CategoryController#makeActive
public function makeActive(Request $request, $id)
{
$category = Category::findOrFail($id);
if($request->has('active'))
{
$category->active = 1;
}else{
$category->active = 0;
}
$category->save();
}
I hope I made sense. If there is anything that isn't clear or if you need me to provide more info, please let me know
Try changing this line
categories: this.categoriesNew
to
categories: category.active
and add a data prop at the top called category.active: ''
I've managed to get it to work. This is what I did.
vue file
<template>
<div class="card-body">
<table class="table">
<thead class="thead-dark">
<tr>
<th scope="col">Active</th>
<th scope="col">Title</th>
<th scope="col">Edit</th>
<th scope="col">Delete</th>
</tr>
</thead>
<tbody>
<tr v-for="(category, index) in categories" >
<td>
<label>checkbox 1</label>
<input type="checkbox" v-model="category.active" #click="checkboxToggle(category)">
</td>
<td>
{{ category.title }}
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
props: ['attributes'],
data(){
return {
categories: this.attributes,
}
},
methods: {
checkboxToggle (category) {
axios.put(`/admin/category/${category.id}/active`, {
active: !category.active
}).then((response) => {
console.log(response)
})
}
},
mounted() {
console.log('Component mounted.')
}
}
</script>
my routes
Route::put('admin/category/{category}/active', 'Admin\CategoryController#makeActive')->name('admin.category.active');
and my CategoryController#makeActive
public function makeActive(Request $request, $id)
{
$category = Category::findOrFail($id);
if(request('active') === true)
{
$category->active = 1;
}else{
$category->active = 0;
}
$category->save();
}

Yajra Datatable custom Model Attribute

I'm using Yajra Datatables for Laravel and it won't show the custom attribute for my User model. This is my User model:
protected $appends = ['sum_work_hours'];
public function work_hours()
{
return $this->hasMany(WorkHour::class);
}
public function getSumWorkHoursAttribute()
{
return $this->work_hours->sum('hours_total');
}
And this is in Controller:
public function showHours()
{
return view('hour');
}
public function getHoursDatatable()
{
$users = User::select(['name', 'email', 'sum_work_hours']);
return Datatables::of($users)->make();
}
And in view:
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.15/css/dataTables.bootstrap.min.css">
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">All Working Hours</div>
<div class="panel-body">
<table id="users-table" class="table table-striped table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Sum Work Hours</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.datatables.net/1.10.15/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.10.15/js/dataTables.bootstrap.min.js"></script>
<script type="text/javascript">
$(function() {
$('#users-table').DataTable({
processing: true,
serverSide: true,
ajax: 'http://127.0.0.1:8000/datatables/get_hours_datatable',
columns: [
{ data: 'name' },
{ data: 'email' },
{ data: 'sum_work_hours' }
]
});
});
</script>
And this is what I get:
What could be wrong?
The problem was in my Controller
public function getHoursDatatable()
{
// this line was wrong
$users = User::select(['name', 'email', 'sum_work_hours']);
return Datatables::of($users)->make();
}
It should have been like this:
$users = User::with('work_hours')->get();
Now I get the results in my table.

laravel delete is not working

I am wondering how to put a csrf token in a form so that delete will work?
Here is my code:
Route:
Route::delete('category_delete/{id}',['as'=>'category_delete','uses'=>'CategoryController#destroy']);
index.blade.php
#section('adminContent')
{!! Form::token() !!}
<div class="table-responsive art-content">
<table class="table table-hover table-striped">
<thead>
<th> NAME</th>
<th> ACTIONS</th>
</thead>
<tbody>
#foreach($categoriesView as $category)
<tr>
<td>{!! $category->name!!}</td>
<td>{!! link_to_route('categories.edit', '', array($category->id),array('class' => 'fa fa-pencil fa-fw')) !!}</td>
<td><button type="button" id="delete-button" class="delete-button" data-url = "{!! url('category_delete')."/".$category->id !!}"><i class="fa fa-trash-o"></i></button>
</td>
</tr>
#endforeach
</tbody>
</table>
<div class="pagination"> {!! $categoriesView->render() !!}</div>
</div>
#stop
CategoryController:
public function destroy($id,Category $category)
{
$category = $category->find ( $id );
$category->delete ();
Session::flash ( 'message', 'The category was successfully deleted!.' );
Session::flash ( 'flash_type', 'alert-success' );
}
If I used ajax, javascript or jquery, how should the code be?
Using jquery I would do something like the following.
Add the line below in the header of your home view
<meta name="csrf-token" content="{{ csrf_token() }}" />
Now in your javascript
function deleteMe(button)
{
// You might want to put the ajaxSetup function where it will always load globally.
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
if(confirm("Are you sure?"))
{
$.get(
'category_delete/'+button.id,
function( response )
{
// callback is called when the delete is done.
console.log('message = '+ response.message);
}
)
}
}
In index.blade.php, make your id more specific.
...
<td><button type="button" id=".$category->id" class="delete-button" data-url = "{!! url('category_delete')."/".$category->id !!}"><i class="fa fa-trash-o" onlick="deleteMe(this);></i></button>
...
In your controller
public function destroy($id,Category $category){
$category = $category->find ( $id );
$category->delete ();
return response()->json(
'message' => 'Deleted',
)
}
Note: No need to add
Form::token in your view
Hope this helps.....
Updated...........
If you were to do it using laravel only, I will suggest you use a link rather than the button you are using.
In index.blade.php, make your id more specific.
...
<td><a href="category_delete/".$category->id class="delete-button" data-url = "{!! url('category_delete')!!}"><i class="fa fa-trash-o"></i></td>
...
In your controller
public function destroy($id,Category $category){
$category = $category->find ( $id );
$category->delete ();
return view('index');//important.
}
If you want your link to look like a button, use css or a css framework like bootstrap
That should be it. hope this helps again.

Resources