Skip to content

Instantly share code, notes, and snippets.

@paulund
Created December 26, 2017 14:53
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save paulund/ae573dc2122d6a88297496bce6da2b8b to your computer and use it in GitHub Desktop.
Save paulund/ae573dc2122d6a88297496bce6da2b8b to your computer and use it in GitHub Desktop.
Creating a Laravel, VueJS, Algolia real time search results box.
<?php
namespace App\Models;
use Laravel\Scout\Searchable;
/**
* Post model
*/
class Post extends Model
{
use Searchable;
/**
* Get the indexable data array for the model.
*
* @return array
*/
public function toSearchableArray()
{
return [
'title' => $this->title,
'slug' => $this->slug,
'tags' => $this->tags()->pluck('title'),
'excerpt' => $this->excerpt
];
}
}
<template>
<section class="search-results" v-show="results.length > 0">
<div v-for="result in results" class="search-result">
<a :href="'/' + result.slug">{{ result.title }}</a>
</div>
</section>
</template>
<script>
import axios from 'axios'
export default {
props: ['query'],
data() {
return {
results: []
};
},
watch: {
query () {
this.getSearchResults();
}
},
methods: {
getSearchResults () {
if(this.query === '' && this.query.length < 3) {
this.results = []
return false
}
axios.get( '/search?q=' + this.query )
.then(response => {
this.results = response.data.data
});
}
}
}
</script>
<style lang="scss">
.search-results {
position: absolute;
top: 0;
background: #FFF;
border: 1px solid #dae4e9;
width: 100%;
max-height: 20rem;
overflow-y: scroll;
> div {
margin-bottom: 0.5rem;
text-align: left;
&:hover {
background-color: #F1F5F8;
}
a {
display: block;
padding: 1rem;
}
}
}
</style>
<template>
<section class="search-results" v-show="results.length > 0">
<div v-for="result in results" class="search-result">
<a :href="'/' + result.slug">{{ result.title }}</a>
</div>
</section>
</template>
<style lang="scss">
.search-results {
position: absolute;
top: 0;
background: #FFF;
border: 1px solid #dae4e9;
width: 100%;
max-height: 20rem;
overflow-y: scroll;
> div {
margin-bottom: 0.5rem;
text-align: left;
&:hover {
background-color: #F1F5F8;
}
a {
display: block;
padding: 1rem;
}
}
}
</style>
<script>
import axios from 'axios'
export default {
props: ['query'],
data() {
return {
results: []
};
},
watch: {
query () {
this.getSearchResults();
}
},
methods: {
getSearchResults () {
if(this.query === '' && this.query.length < 3) {
this.results = []
return false
}
axios.get( '/search?q=' + this.query )
.then(response => {
this.results = response.data.data
});
}
}
}
</script>
<template>
<section class="search">
<search-box v-model="query" placeholder="Search for a tutorial" :show-results.sync="showResults"></search-box>
<div class="relative" v-show="showResults">
<search-results :query="query"></search-results>
</div>
</section>
</template>
<script>
import SearchBox from './SearchBox'
import SearchResults from './Results'
export default {
data() {
return {
query: '',
showResults: true
};
},
components: {
SearchBox,
SearchResults
}
}
</script>
<template>
<div>
<input type="search"
:value="value"
:placeholder="placeholder"
v-on:input="updateValue($event.target.value)"
id="s"
name="s"
autocomplete="off">
</div>
</template>
<script>
export default {
props: ['value', 'placeholder'],
methods: {
updateValue: function (value) {
this.$emit('input', value)
}
}
}
</script>
<?php
namespace App\Http\Controllers\Search;
use App\Http\Controllers\Controller;
use App\Models\Post;
use Illuminate\Http\Request;
/**
* Class SearchController
* @package App\Http\Controllers\Search
*/
class SearchController extends Controller
{
public function search(Request $request)
{
$this->validate($request, [
'q' => 'required'
]);
$posts = Post::search( $request->get('q') )->raw();
if(empty($posts['hits'])) {
$posts['hits'] = [];
}
return response()->json(['data' => $posts['hits']]);
}
}
<section>
<ais-index app-id="{{ config('scout.algolia.id') }}"
api-key="{{ config('scout.algolia.search') }}"
index-name="posts_index">
<div class="relative">
<ais-input placeholder="Search for tutorials..."></ais-input>
<ais-results>
<template scope="{ result }">
<div>
<h4><a :href="result.slug">@{{ result.title }}</a></h4>
<p><a :href="result.slug">@{{ result.excerpt }}</a></p>
</div>
</template>
<ais-powered-by slot="footer" />
</ais-results>
</div>
</ais-index>
</section>
@abrhambas01
Copy link

Is this thing going right ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment