Laravel search results in second page - laravel

I'm working with vuejs in laravel 5.6 and I've made search form with 2 inputs, but i'm not sure how to show results in different page and my data return by json.
Logic
User fill inputs/input -> click search button
User redirects to other page -> see results
Code
controller
public function indexsearch(Request $request)
{
$area = request('area');
$keywords = request('keywords');
if($keywords){
$projects = Project::where('title', 'LIKE', "%{$keywords}%")
->orWhere('body', 'LIKE', "%{$keywords}%")
->orWhere('area', 'LIKE', "%{$keywords}%")
->get();
}else{
$projects = Project::where('title', 'LIKE', "%{$keywords}%")
->orWhere('body', 'LIKE', "%{$keywords}%")
->orWhere('area', 'LIKE', "%{$keywords}%")
->get();
}
return response()->json($projects);
}
route
Route::get('indexsearch', 'Api\SearchController#indexsearch');
component
<template>
<div>
<div class="form-row justify-content-center align-items-center mmt-5">
<div class="col-md-4">
<label class="sr-only" for="inlineFormInput">Query</label>
<input v-model.lazy="keywords" type="text" class="customformdes form-control mb-2" id="inlineFormInput" placeholder="E.g. Larave, Vue, Design, Writer">
</div>
<div class="col-md-4">
<label class="sr-only" for="inlineFormInputGroup">Area</label>
<input v-model.lazy="area" type="text" class="customformdes form-control mb-2" id="inlineFormInputGroup" placeholder="E.g. Jabodetabek, Medan, Surabaya">
</div>
<div class="col-md-2">
<button type="button" class="customformdes btn btn-block btn-primary mb-2"><i class="fab fa-searchengin"></i> Search</button>
</div>
</div>
// currently this part shows the result as hidden drop-down
<b-list-group v-if="results.length > 0">
<b-list-group-item v-for="result in results" :key="result.id" :to="`/projects/${result.slug}`">{{result.title}}</b-list-group-item>
</b-list-group>
</div>
</template>
<script>
var _ = require('lodash');
import navbar from './navbar.vue';
export default {
data() {
return {
keywords: null,
area: null,
results: []
};
},
watch: {
keywords(after, before) {
this.fetch();
}
},
methods: {
fetch() {
axios.get('/api/indexsearch', { params: { keywords: this.keywords, area: this.area } })
.then(response => this.results = response.data)
.catch(error => {});
}
}
}
</script>
Issues
By my current control code I cannot get my second input (area) alone, meaning: If I don't fill first input and just fill second input i cannot get results.
I don't know how to show results in other page and not as drop-down in the same page (part is commented in my component code)
Thanks in advance.

For your first issue, you can add more watcher for the area:
(Since you are just calling a single method from your watcher, you can just directly use the method's name as the value of your watcher as it is just the same.)
watch: {
keywords: 'fetch',
area: 'fetch'
},
you can also use $vm.watch to watch for multiple values at the same time.
Instead of:
watch: {
keywords(after, before) {
this.fetch();
}
},
you can have it like this:
mounted() {
this.$watch(
function() {
return this.keywords + this.area
},
(newVal, oldVal) => {
this.fetch();
}
)
}
As for the second issue, you can pass props data to the target page.
See this example code below:
ResultsPage.vue:
<template>
<div>
<div v-for="result in results" :key="result.id">{{ result.title }}</div>
</div>
</template>
<script>
export default {
props: ['results']
}
</script>
router configuration:
import ResultsPage from 'ResultsPage.vue'
new Router({
routes: [
// ... some other routes in here
{
path: '/results',
name: 'results',
props: true,
component: ResultsPage,
}
]
})
then you can now navigate to the ResultsPage by using $router.push():
methods: {
fetch() {
axios.get('/api/indexsearch', {
params: {
keywords: this.keywords,
area: this.area
}
})
.then(response => {
this.$router.push({
name: 'results',
params: {
results: response.data
}
})
})
.catch(error => {});
}
}

You will need to add $area as your search term to solve the first issue.
And to show the results on a separate page you will need to request a route that returns a view in your search method.
// Controller
public function indexsearch(Request $request)
{
$area = request('area');
$keywords = request('keywords');
if($keywords){
$projects = Project::where('title', 'LIKE', "%{$keywords}%")
->orWhere('body', 'LIKE', "%{$keywords}%")
->orWhere('area', 'LIKE', "%{$area}%")
->get();
}else{
$projects = Project::where('title', 'LIKE', "%{$keywords}%")
->orWhere('body', 'LIKE', "%{$keywords}%")
->orWhere('area', 'LIKE', "%{$area}%")
->get();
}
return view('search_results', compact('projects'));
}
If you want to show the results on a separate page then you don't really need vue here.
You can do something like this to submit a form in your view:
<form action="search"> <!-- or whatever your searchindex route is -->
<div class="form-row justify-content-center align-items-center mmt-5">
<div class="col-md-4">
<label class="sr-only" for="inlineFormInput">Query</label>
<input v-model.lazy="keywords" type="text" class="customformdes form-control mb-2" id="inlineFormInput" placeholder="E.g. Larave, Vue, Design, Writer">
</div>
<div class="col-md-4">
<label class="sr-only" for="inlineFormInputGroup">Area</label>
<input v-model.lazy="area" type="text" class="customformdes form-control mb-2" id="inlineFormInputGroup" placeholder="E.g. Jabodetabek, Medan, Surabaya">
</div>
<div class="col-md-2">
<button type="button" class="customformdes btn btn-block btn-primary mb-2"><i class="fab fa-searchengin"></i> Search</button>
</div>
</div>
</form>
Then in your search results blade (search_results.blade.php) view:
<div>
<b-list-group">
#foreach($projects as $project)
<b-list-group-item>{{ $project->title }}</b-list-group-item>
#endforeach
</b-list-group>
</div>
I hope this helps solve your issue.

Related

Laravel WhereHas() / orWhereHas not working

I want to fetch data from two tables Employee and Company via search by typing Company name not by company Id the tables are linked with one to many Relationship.they rest search functionality is working perfectly.any help would be highly appreciated.
Code in EmployeeController is :
public function searchemp(){
if($search = \Request::get('q')){
$employees = Employee::with('company')->with('nationality')->where(function($query) use ($search){
$query->Where('BadgeCode','LIKE',"%$search%")
->orWhereHas('company', function ($query) use ($search) {
where('Company', 'like', '%'.$search.'%'); })
->orWhere('tazker','LIKE',"%$search%")
->orWhere('lastname','LIKE',"%$search%")
->orWhere('firstname','LIKE',"%$search%")
->orWhere('serialnumber','LIKE',"%$search%")
->orWhere('BadgeType','LIKE',"%$search%");
})->orderBy('BadgeCode','desc')->paginate(20);
} else{
$employees = Employee::with('company')->with('nationality')->latest()->paginate(5);
}
return $employees;
}
HTML Code in Employee.vue is :
<div style="margin-left:450px; margin-top:0px;margin-bottom:-10px;">
<h3 class="card-title">
<div class="card-tools">
<div class="input-group input-group-sm" style="width: 250px;">
<input
name="table_search"
class="form-control float-right"
placeholder="Search"
#keyup="searchit"
v-model="search"
type="search"
aria-label="Search"
/>
<div class="input-group-append">
<button type="submit" class="btn btn-default" #click="searchit">
<i class="fas fa-search"></i>
</button>
</div>
</div>
</div>
</h3>
</div>
Script Code in Employee.vue is :
export default {
data() {
return {
employees :{},
search: "",
},
methods: {
searchit: _.debounce(() => {
Fire.$emit("searching");
}, 300),
},
created() {
Fire.$on("searching", () => {
let query = this.search;
axios
.get("api/findemployee?q=" + query)
.then(data => {
this.employees = data.data;
})
.catch(() => {});
});
}
}
API Rout is :
Route::get('findemployee','API\EmployeeController#search');

After Login the get route not worked in vue js Component in laravel

i want to load my provinces list in vue.js component , my code is worked , but when i login , show nothing in my list , here is my code
in my vuejs component :
<template>
<div class="row">
<div class="form-group required col-6">
<label for="province_id">Province</label>
<div class="col-12">
<select class="form-control" name="province_id[]" id="province_id" multiple v-model="province" style="height: 200px"
#change="getAllCities()">
<option v-for="province in provinces" :value="province.id">{{province.name}}</option>
</select>
</div>
</div>
<div class="form-group required col-6" v-if="cities.length> 0">
<label for="city_id">City</label>
<div class="col-12">
<select class="form-control" multiple="" name="city_id" id="city_id" style="height: 200px">
<option v-for="city in cities" :value="city.id">{{city.name}}</option>
</select>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
province: 'Select Province',
cities: [],
provinces: [],
flag: false,
}
},
mounted() {
axios.get('/api/provinces').then(res => {
// console.log(res);
this.provinces = res.data.provinces;
}).catch(err => {
// console.log (err)
})
},
methods: {
getAllCities: function () {
console.log()
axios.get('/api/cities/(' + this.province + ')').then(res => {
console.log(res);
this.cities = res.data.cities
}).catch(err => {
// console.log (err)
})
},
}
}
</script>
in Register Controller :
public function getAllProvinces()
{
$provinces=Province::all();
$response=['provinces'=>$provinces];
return response()->json($response,200);
}
my code work before login and show all provinces in my db , but after login there is nothin in my select , please help about that .
It happens because RegisterController uses guest middleware, you just don't pass the middleware when logged in, also it isn't good to me to use any method not related to the controller. Create new ProvinceController and define your method there it'll work
// this is RegisterController constructor
public function __construct()
{
$this->middleware('guest');
}
But if you somehow want to use your method exactly in the RegisterController then use except() method in constructor:
// add except
public function __construct()
{
$this->middleware('guest')->except(['getAllProvinces']);
}

how to redirect to a view from ajax on click in laravel?

so i am making an autocomplete live search in laravel & $ajax .
how do i redirect to a specific view in click ? i mean when i click one of the results it would send me to the product page , for now all it does is reload the page...but whan i click on the search button it just return to the same page and nothing changes in the URL address . thank you .
Route
Route::get('/search/{search}', 'SearchController#search');
blade.php
<div class="vcenter-this">
<form action="" method="GET" autocomplete="off" novalidate="novalidate">
#csrf
<div class="input-group ml-3" style="max-width: max-content; margin-left:
10px;">
<input type="text" class="form-control" value="<?= old('search'); ?>"
name="search" id="search" class="form-control" placeholder="Search Item">
<span class="input-group-btn">
<input id="search-btn" type="submit" value="GO!" name="submit"
class="btn btn-primary" >
<i class="fa fa-search"></i>
</span>
</div>
Controller
use Illuminate\Http\Request;
use DB;
use App\Search;
class SearchController extends MainController
{
public function search($search)
{
if (isset($_GET['submit'])) {
$search = filter_input(INPUT_GET, 'search', FILTER_SANITIZE_STRING);
$search = trim($search);
}
if ($search) {
$products = DB::table('products as p')
->join('categories as c', 'c.id', '=', 'p.categorie_id')
->select('p.ptitle', 'p.purl', 'c.curl')
->where('p.ptitle', 'like', '%' . $search . '%')
->get()
->toJson();
if ($products) {
return $products;
}
}
}
}
Script.js
$('#search').on('input', function () {
var userSearch = $(this).val().trim();
if (userSearch.length > 0) {
$.ajax({
type: 'GET',
url: BASE_URL + 'search/' + userSearch ,
dataType: 'json',
//data: { search: userSearch },
success: function (products) {
if (products) {
var availableTags = [];
products.forEach(function (products) {
availableTags.push(products.ptitle);
});
$("#search").autocomplete({
source: availableTags,
select: function (event, ui) {
$('#search-btn').on('click',function(products) {
***what commend should i put here ? ***
***the destination url supposed to be like the line below ***
// window.location.href = BASE_URL + "shop/" + products.curl + '/' + products.purl;
});
}
});
}
}
});
}
});

Getting a vue form to go to the next page after submit in laravel

I'm using both laravel and vue and I'm trying to get my vue form to go to another page when I've submitted, but the problem is that once it's submitted it refreshes and goes back to the page it's on instead of the other page.
When I check my network tab in the dev tools, it posted to the right page but it doesn't show up in the browser
This is my vue
<template>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Example Component</div>
<div class="card-body">
I'm an Supplier Code Selection component.
<br>
<form #submit.prevent="submit">
<label for="parent-product">Parent Product</label>
<select name="parent-product" id="parent-product" class="form-control" v-model="selected_parent">
<option>Please select your code</option>
<option v-for="product in products" :value="product.id">
{{ product.supplier_code }}
</option>
<option v-for="child in children" :value="child.id">
{{ child.supplier_code }}
</option>
</select>
<input type="submit" class="btn btn-primary" value="Submit">
</form>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
props: [
'products',
'children',
'selected_parent'
],
mounted() {
console.log('Component mounted.')
},
methods: {
submit(){
var formData = new FormData();
console.log('this is the select box - '+this.selected_parent);
formData.append('parent-product', this.selected_parent);
return axios.post('/add-to-cart/'+this.selected_parent, formData);
},
},
}
</script>
My route
Route::any('/add-to-cart/{id}', 'PublicController#getAddToCart')->name('product.addToCart');
My controller function
public function getAddToCart(Request $request, $id)
{
$menus_child = Menu::where('menu_id', 0)->with('menusP')->get();
$contacts = Contact::all();
$product = Product::find($id);
$supplier_code = $request->supplier_code;
$oldCart = Session::has('cart') ? Session::get('cart') : null;
$cart = new Cart($oldCart);
$cart->add($product, $product->id, $supplier_code);
$request->session()->put('cart', $cart);
return view('public.shopping-cart', ['products' => $cart->items, 'totalPrice' => $cart->totalPrice, 'menus_child' => $menus_child, 'contacts' => $contacts, 'supplier_code' => $supplier_code]);
}
You could just add a location if the axios call returns success. Ps. untested code.
methods: {
submit(){
var formData = new FormData();
console.log('this is the select box - '+this.selected_parent);
formData.append('parent-product', this.selected_parent);
return axios.post('/add-to-cart/'+this.selected_parent, formData)
.then(response => { location: '/'});
},
I found my issue. I forgot to change
return view('public.shopping-cart', compact('supplier_code'));
to
return ['redirect' => route('product.shoppingCart')];
in my laravel controller function

No error messages from 422 response on laravel form request from vue component

I'm trying to submit a form request using axios, have it validated, and return errors if the validation fails. The problem is, when I submit the form, no error messages are returned for me to show on the client side. Here's the HTTP request and vue component:
<div class="card">
<div class="card-header">
<h4>Information</h4>
</div>
<div class="card-body">
<p v-if='!isEditMode'><strong>Name:</strong> {{businessData.name}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-name">Name</label></strong>
<input class="form-control" name="business-name" v-model='businessData.name'>
</div>
<p v-if='!isEditMode'><strong>Description:</strong> {{businessData.description}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-description">Description</label></strong>
<textarea class="form-control normal" name="business-description"
placeholder="Enter your services, what you sell, and why your business is awesome"
v-model='businessData.description'></textarea>
</div>
</div>
</div>
<div class="card">
<h4 class="card-header">Address Information</h4>
<div class="card-body">
<p v-if="!isEditMode"><strong>Street Address: </strong> {{businessData.street}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-street">Street Address: </label></strong>
<input type="text" class="form-control" name="business-street" v-model='businessData.street' placeholder="1404 e. Local Food Ave">
</div>
<p v-if="!isEditMode"><strong>City: </strong> {{businessData.city}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-city">City: </label></strong>
<input class="form-control" type="text" name="business-city" v-model='businessData.city'>
</div>
<p v-if="!isEditMode"><strong>State: </strong> {{businessData.state}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-state">State: </label></strong>
<select class="form-control" name="business-state" id="state" v-model="businessData.state" >...</select>
</div>
<p v-if="!isEditMode"><strong>Zip: </strong> {{businessData.zip}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-zip">Zip: </label></strong>
<input class="form-control" type="text" maxlength="5" name="business-zip" v-model='businessData.zip'>
</div>
</div>
</div>
<div class="card">
<h4 class="card-header">Contact Information</h4>
<div class="card-body">
<p v-if="!isEditMode"><strong>Phone: </strong> {{businessData.phone}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-phone">Phone: </label></strong>
<input class="form-control" type="tel" name="business-phone" v-model='businessData.phone'>
</div>
<p v-if="!isEditMode"><strong>Email: </strong> {{businessData.email}}</p>
<div class="form-group" v-if='isEditMode'>
<strong><label for="business-Email">Email: </label></strong>
<input class="form-control" type="email" name="business-email" v-model='businessData.email'>
</div>
</div>
</div>
</div>
<script>
export default {
data () {
return {
isEditMode: false,
businessData: this.business,
userData: this.user,
errors: []
}
},
props: {
business: {},
user: {},
role: {}
},
//Todo - Institute client side validation that prevents submission of faulty data
methods: {
validateData(data) {
},
saveBusinessEdits () {
axios.put('/businesses/' + this.business.id , {updates: this.businessData})
.then(response => {
console.log(response.data)
// this.businessData = response.data;
this.isEditMode = false;
})
.catch (response => {
console.log(response.data)
this.isEditMode = false;
})
},
saveUserEdits () {
axios.put('/profile/' + this.user.id , {updates: this.userData})
.then(response => {
console.log(response.data)
this.userData = response.data;
this.isEditMode = false;
})
.catch (response => {
console.log(response)
this.isEditMode = false;
})
}
}
}
Route
Route::put('/businesses/{id}', 'BusinessesController#update');
BusinessController and update function
public function update(BusinessRequest $request, $id)
{
$business = Business::find($id)->update($request->updates);
$coordinates = GoogleMaps::geocodeAddress($business->street,$business->city,$business->state,$business->zip);
if ($coordinates['lat']) {
$business['latitude'] = $coordinates['lat'];
$business['longitude'] = $coordinates['lng'];
$business->save();
return response()->json($business,200);
} else {
return response()->json('invalid_address',406);
}
$business->save();
return response()->json($business,200);
}
and BusinessRequest class
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'business-name'=> 'required|string|max:255',
'business-description'=> 'required|string',
'business-phone' => 'nullable|phone|numeric',
'business-email' => 'nullable|email',
'business-street'=> 'required|string',
'business-city' => 'required|string',
'business-state' => 'required|string|max:2',
'business-zip' => 'required|min:5|max:5|numeric',
];
}
public function messages() {
return [
'business-zip.min:5' =>'your zip code must be a 5 characters long',
'business-email.email'=>'your email is invalid',
'business-phone.numeric'=>'your phone number is invalid',
];
}
}
I don't understand why, even if input valid data, it responds with a 422 response and absolutely no error messages. Since this is laravel 5.6, the 'web' middleware is automatic in all of the routes in the web.php file. So this isn't the problem. Could anyone help me normalize the validation behavior?
In Laravel a 422 status code means that the form validation has failed.
With axios, the objects that are passed to the then and catch methods are actually different. To see the response of the error you would actually need to have something like:
.catch (error => {
console.log(error.response)
this.isEditMode = false;
})
And then to get the errors (depending on your Laravel version) you would have something like:
console.log(error.response.data.errors)
Going forward it might be worth having a look at Spatie's form-backend-validation package
You can use Vue.js and axios to validate and display the errors. Have a route called /validate-data in a controller to validate the data.
app.js file:
import Vue from 'vue'
window.Vue = require('vue');
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
class Errors {
constructor() {
this.errors = {};
}
get(field) {
if (this.errors[field]) {
return this.errors[field][0];
}
}
record(errors) {
this.errors = errors;
}
clear(field) {
delete this.errors[field];
}
has(field) {
return this.errors.hasOwnProperty(field);
}
any() {
return Object.keys(this.errors).length > 0;
}
}
new Vue({
el: '#app',
data:{
errors: new Errors(),
model: {
business-name: '',
business-description: '',
business-phone: ''
},
},
methods: {
onComplete: function(){
axios.post('/validate-data', this.$data.model)
// .then(this.onSuccess)
.catch(error => this.errors.record(error.response.data.errors));
},
}
});
Make a route called /validate-data with a method in the controller, do a standard validate
$this->validate(request(), [
'business-name'=> 'required|string|max:255',
'business-description'=> 'required|string',
'business-phone' => 'nullable|phone|numeric',
'business-email' => 'nullable|email',
'business-street'=> 'required|string',
'business-city' => 'required|string',
'business-state' => 'required|string|max:2',
'business-zip' => 'required|min:5|max:5|numeric'
]
);
Then create your inputs in your view file, using v-model that corresponds to the vue.js data model fields. Underneath it, add a span with an error class (basic red error styling, for example) that only shows up if the errors exist. For example:
<input type="text" name="business-name" v-model="model.business-name" class="input">
<span class="error-text" v-if="errors.has('business-name')" v-text="errors.get('business-name')"></span>
Don't forget to include the app.js file in footer of your view file. Remember to include the tag, and run npm run watch to compile the vue code. This will allow you to validate all errors underneath their input fields.
Forgot to add, have a buttton that has #onclick="onComplete" to run the validate method.

Resources