I've made a search function to show related projects based on chosen tag and I'm getting results with wrong values
What I've done so far
Create controller function and return results as json
Create route in app.js
Create new component to show results
made axios request to send data to controller and redirect to new component for results
Code
controller
public function areas(Request $request){
$areas = Project::where('area', $request->area)->where('published', '=', 'y')->get();
return response()->json($areas, 200);
}
route in api.php
Route::get('areasearch', 'Api\SearchController#areas');
route in app.js
import AreasPage from './components/areassearch.vue'
{
path: '/areas',
name: 'areas',
props: true,
component: AreasPage,
},
search script + component link
// link
<a v-model.lazy="area" #click="areasearch">{{project.area}}</a>
//script
methods: {
//search in areas
areasearch() {
axios.get('/api/areasearch', {
params: {
area: this.area
}
})
.then(response => {
this.$router.push({
name: 'areas',
params: {
areas: response.data
}
})
})
.catch(error => {});
},
},
results component
<template>
<div>
<navbar></navbar>
<template v-if="areas.length > 0">
<div class="container-fluid counters">
<div class="row text-center">
<div v-for="area in areas" :key="area.id" :to="`/projects/${area.slug}`">
<li>{{area.title}}</li>
</div>
</div>
</div>
</template>
<template v-else>
<p>Sorry there is no area for you, try search new query.</p>
</template>
<footerss></footerss>
</div>
</template>
<script>
import navbar from './navbar.vue';
import footerss from './footer.vue';
export default {
props: ['areas'],
components: {
navbar,
footerss
},
}
</script>
Issue
My link is not behave as a link (is like text when i move mouse over it)
For example if I search for area Jakarta most of results I get is projects where their area column is null.
Any idea?
For the link part, you are using v-model on an anchors, v-model is mainly for inputs, selects, textareas. So
<a v-model.lazy="area" #click="areasearch">{{project.area}}</a>
Should be
<span class="my-link" #click="areasearch(project.area)">{{project.area}}</span>
Use a span, and a class for that span, then on click call your method, i don't know if thats the correct variable for your axios call, btw. it could be project.area.id, or something else...
As for it looking like a link, i assume you are familiar with cursor:pointer css rule.
Your axios part should look something like this:
areasearch(thearea) {
axios.get('/api/areasearch', {
params: {
area: thearea
}
})
.then(response => {
this.$router.push({
name: 'areas',
params: {
areas: response.data
}
})
})
.catch(error => {});
},
As for the controller part:
public function areas(Request $request){
$auxAreas = explode("+", $request->area);
$areas = Project::whereNotNull('area')
->whereIn('area', $auxAreas)
->where('published', '=', 'y')
->get();
return response()->json($areas, 200);
}
first for the wrong result issue try this:
public function areas(Request $request){
$areas = Project::whereNotNull('area')
->where([
['area', $request->area],
['published', '=', 'y']
])->get();
return response()->json($areas, 200);
}
Related
I am trying to fetch some reports with axios in Laravel. I tested it in Postman and everything is fine.
But here I got error 401.
Here is my code:
#Reports.vue file
<template>
<div>
<h1>All reports: </h1>
<div v-for="report in reports" v-bind:key="report.id">
<p>{{ report.description }}</p>
</div>
</div>
</template>
<script>
export default {
name: "Reports",
data () {
return {
reports : [],
}
},
mounted() {
this.fetchReports();
},
methods : {
async fetchReports() {
const {data} = await axios.get('/api/report');
this.reports = data.data;
},
}
}
</script>
#ReportController#index
public function index()
{
$reports = Report::all();
return ReportResource::collection($reports);
}
#api.php
Route::group(['middleware' => 'auth:api'], function () {
Route::apiResource('/report', 'API\ReportController');
});
Thanks in advance. If You need more informations, I will post them.
Laravels auth:api middleware require you to pass Bearer token or ?api_token= url param with your request
Source
And I don't see you passing token with your axios request
Im struggling to get a Laravel Cashier Strip integration to work with 3d secure cards.
I have go it setup so subscription works and is showing up in my stripe dashboard and everything it getting to my local database.
But when I test with cards that needs strong authntication as 3ds they get the status of incomplete in my stripe dashboard.
I get the cashier.payment response page in my console log. but isn't Cashier supposed to redirect to this confirmation window?
My code is as follows
In my subscription controller i have
public function index() {
$data = [
'intent' => auth()->user()->createSetupIntent(),
// 'plans' => $available_plans
];
return view('artists.subscription')->with($data);
}
public function checkout(Request $request) {
$user = auth()->user();
$paymentMethod = $request->payment_method;
$planId = 'monthly_sub';
// SCA
try {
$subscription = $user->newSubscription('monthly', $planId)->create($paymentMethod);
} catch (IncompletePayment $exception) {
return redirect()->route(
'cashier.payment',
[$exception->payment->id, 'redirect' => route('front')]
);
}
// return response(['status' => 'Success']);
}
and in my stripe js file I have this
const stripe = Stripe('stripe_key'); // i have my test key here
const elements = stripe.elements();
const cardElement = elements.create('card',{hidePostalCode: true});
cardElement.mount('#card-element');
const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
const clientSecret = cardButton.dataset.secret;
cardButton.addEventListener('click', async (e) => {
const { setupIntent, error } = await stripe.confirmCardSetup(
clientSecret, {
payment_method: {
card: cardElement,
billing_details: { name: cardHolderName.value }
}
}
);
axios.post('checkout', {
payment_method: setupIntent.payment_method
}).then(response => {
console.log(response.request.responseURL)
})
.catch(error => {
console.log(error.response)
});
});
My blade view is
#extends('layouts.app')
#section('head')
#php
// dd($intent);
#endphp
<script src="https://js.stripe.com/v3/"></script>
<link href="{{ asset('css/stripe.css') }}" rel="stylesheet">
#endsection
#section('content')
<input id="card-holder-name" type="text">
<!-- Stripe Elements Placeholder -->
<div id="card-element"></div>
<button id="card-button" data-secret="{{ $intent->client_secret }}">
subscribe
</button>
#endsection
#section('js')
<script src="{{ asset('js/stripe.js') }}" type="text/javascript"></script>
#endsection
Everything seems to be working but I just get the 3ds confirmation page as a response in my console. How do I get laravel to redirect and open that page for the user?
I think the issue is the order of events, your code should wait for 3DS to be called and completed before calling back to your code after the result is handed back to you. Using this regulator card [1] you should be able to test that with some small changes like this (I had to remove some things but this should be a reference):
stripe.confirmCardSetup(
"seti_xxx", {
payment_method: {
card: card,
billing_details: { name: "Name" }
}
}).then(function(result) {
// Check the result status here!!
axios.post('checkout', {
payment_method: result.setupIntent.payment_method
}).then(response => {
console.log(response.request.responseURL)
}).catch(error => {
console.log(error.response)
});
});
Hope this helps!
[1] https://stripe.com/docs/testing#regulatory-cards
I am using vuejs with laravel and I have links for next and previous pages while url is changing but new data won't show.
Code
Controller
$verse = Chapter::where('slug', $slug)->with(['verses', 'book'])->first();
$next = Chapter::where('id', '>', $verse->id)->first();
$previous = Chapter::where('id', '<', $verse->id)->first();
return response()->json([
'verses' => $verse,
'next' => $next,
'previous' => $previous,
]);
component
<div v-if="previous !== null" :v-model="previous" class="bible-nav-button previous">
<router-link :to="`/${testament_slug}/${book_slug}/${previous.slug}`">
<i class="fas fa-chevron-left"></i> <span>Previous</span>
</router-link>
</div>
<div v-if="next !== null" :v-model="next" class="bible-nav-button next">
<router-link :to="`/${testament_slug}/${book_slug}/${next.slug}`">
<span>Next</span> <i class="fas fa-chevron-right"></i>
</router-link>
</div>
<script>
export default {
name: "books",
data() {
return {
url: window.location.origin + "" + window.location.pathname,
title: '',
testament_slug:'',
book_slug: '',
slug: '',
verses: [],
next: '',
previous: ''
}
},
methods: {
getBooks: function(){
this.testament_slug = this.$route.params.testament_slug
this.book_slug = this.$route.params.book_slug
this.slug = this.$route.params.slug
axios.get('/api/'+this.$route.params.book_slug+'/'+this.$route.params.slug+'/'+this.$route.params.slug).then((res) => {
this.verses = res.data.verses
this.title = "Read Bible: " +res.data.verses.book.name+ " " +res.data.verses.name
this.next = res.data.next
this.previous= res.data.previous
})
.catch((err) => {
console.log(err)
});
},
myHTML: function(item) {
return "<strong>"+item.number+"</strong> "+item.body+" ";
}
},
mounted() {
this.getBooks();
}
}
</script>
Any idea how i can fetch my new data?
You can use :key props in <router-view> tag and use the route fullpath.
<router-view :key="$route.fullPath"></router-view>
I used to put a watcher of the current route and call function to fetch data, but I just found that we can do this after watching vue-router tutorial from vueschool.
Also see the answer of this question
You have two simple options:
1) Change the "previous" and "next" to #click actions that call the fetch function again and then update the URL (like this this.$router.push()).
<span #click="goNext(/* needed arguments here */)">Next</span>
goNext (/* params here */) {
//set whatever variables you need to
this.$router.push(/* route as string here */)
this.getBooks()
}
2) Since you're using the router, use guards to update every time the URL changes:
https://router.vuejs.org/guide/advanced/navigation-guards.html
beforeRouteUpdate(to, from, next) {
this.getBooks()
next()
}
I have a custom resource-tool working fine in the view panel of a resource, but it dont appears when i go o the edit mode. Is there something i should add to the component or to the Nova configuration to enable the component in the edit mode?
Code in User.php
public function fields(Request $request)
{
return [
ID::make()->sortable(),
Text::make('First name', 'firstName')
->sortable()
->rules('required', 'max:255'),
Text::make('Last name', 'lastName')
->sortable()
->rules('required', 'max:255'),
Text::make('Email')
->sortable()
->rules('required', 'email', 'max:254')
->creationRules('unique:users,email')
->updateRules('unique:users,email,{{resourceId}}'),
Password::make('Password')
->onlyOnForms()
->creationRules('required', 'string', 'min:6')
->updateRules('nullable', 'string', 'min:6'),
YesNovaUserPermissions::make(),
];
}
User view:
User edit:
Nova does not seem to allow you to obtain this functionality with a custom resource but you can with a custom field. You basically create a "dummy" field which does not really exist on the model and use a mutator on the model to overwrite the default model saving functionality.
Following the documentation above, you can build a Vue component which will appear within the resource edit form itself, similarly to how I have done with the tags picker pictured below.
Code for that:
<template>
<default-field :field="field" :errors="errors" :show-help-text="showHelpText">
<label for="tag" class="inline-block text-80 pt-2 leading-tight">Tag</label>
<template slot="field">
<div id="multitag-flex-holder">
<div id="multitag-search-holder" class="w-1/2">
<div class="search-holder">
<label>Search Categories</label>
<input type="text" v-model="searchString" #focus="isSearching = true" #blur="isSearching = false" style="border:2px solid #000"/>
<div class="found-tags" v-if="isSearching">
<div v-for="(tag, i) in foundTags" #mousedown="addToSelected(tag)" :key="i">{{tag.name}}</div>
</div>
</div>
</div>
<div class="select-tags-holder w-1/2">
<div class="selected-tags">
<div v-for="(tag, i) in selectedTags" :key="'A'+i" #click="removeTag(tag)">{{tag.name}} X</div>
</div>
</div>
</div>
</template>
</default-field>
</template>
<script>
import { FormField, HandlesValidationErrors } from 'laravel-nova'
export default {
mixins: [FormField, HandlesValidationErrors],
props: ['resourceName', 'resourceId', 'field'],
data: function () {
return {
selectedTags:[],
isSearching:false,
searchString:''
}
},
mounted(){
console.log(this.field)
this.field.value.forEach((tag)=>{
this.addToSelected(tag)
})
formData.append('whereType', 'Tag');
},
computed: {
// a computed getter
foundTags() {
// `this` points to the vm instance
return this.field.tags.filter((tag) => {
if(tag.name.search(new RegExp(this.searchString, "i")) >= 0){
if(this.selectedTagNames.indexOf(tag.name) == -1){
return tag;
}
};
})
},
selectedTagNames(){
var selNames = this.selectedTags.map((tag) => {
return tag.name;
})
return selNames;
}
},
methods: {
/*
* Set the initial, internal value for the field.
*/
setInitialValue() {
this.value = this.field.value || ''
},
removeTag(tag){
var index = this.selectedTags.indexOf(tag);
if (index > -1) {
this.selectedTags.splice(index, 1);
}
},
addToSelected(tag){
this.selectedTags.push(tag)
},
/**
* Fill the given FormData object with the field's internal value.
*/
fill(formData) {
var tagIdArray = []
this.selectedTags.forEach((tag)=>{
tagIdArray.push(tag.id)
})
formData.append(this.field.attribute, tagIdArray)
},
},
}
</script>
Then, you can overwrite how the save functionality works in your model to accommodate for the "dummy" field. Note below instead of syncing the tags directly on the mutator, which will work most of the time depending on your data structure, I had to pass the tags to the "Saved" event on the model to accommodate for when creating a new record and the associated record id is not yet available, thus cannot be synced for a many to many relationship.
public function setTagsAttribute($value)
{
$tags = explode(",", $value);
$this->tempTags = $tags;
unset($this->tags);
}
protected static function booted()
{
static::saved(function ($article) {
$article->tags()->sync($article->tempTags);
});
}
Hello I need to do autocompletion to some cities i already have in my db
so my code is like this :
View
<input type="text" name="ville" id="ville" class="small" placeholder="Entrer la ville souhaité">
<script type="text/javascript">
$(function() {
$( "#ville" ).autocomplete({
source:'{!!URL::route('autocomplete')!!}',
minlength:1,
autoFocus:true,
select:function(e,ui)
{
$('#ville').val(ui.item.value);
}
});
});
</script>
Controller
class VilleController extends Controller
{
public function autocomplete(Request $request)
{
$term = $request->term;
$queries = DB::table('ville')
->where('libelle_ville', 'like', '%'.$term.'%')
->take(6)->get();
foreach ($queries as $query)
{
$results[] = ['id' => $query->id, 'value' => $query->libelle_ville]; //you can take custom values as you want
}
return response()->json($results);
}
}
Routes
Route::get('/autocomplete', array('as' => 'autocomplete', 'uses'=>'VilleController#autocomplete'));
It doesn't tells me that I have an error and it doesn't show me any completion either.
Debug json request with laravel is a bit difficult, I recommend you to download this package
https://github.com/ARCANEDEV/LogViewer
or manually open the laravel log in storage/logs/laravel.log and see whats happened
Thanks to Stack and EddyTheDove I found out that the error is that aucomplete is not a function so I have to remove the barkets and $function so it would be something like this in the script tag
<script type="text/javascript">
$( "#ville" ).autocomplete({
source:'{!!URL::route('autocomplete')!!}',
minlength:1,
autoFocus:true,
select:function(e,ui)
{
$('#ville').val(ui.item.value);
}
});
</script>