tags an search
commit d72af19ac7c028c514ee9055386c65bf4fcc0ffc
Author: Olivier Legat <olivier.legat@ldc.fr>
Date: Fri Apr 9 15:08:30 2021 +0200
recherche - filtrage basique
commit 926efbe97826d6ab8168ccb93f329283c11fd4ec
Author: Olivier Legat <olivier.legat@ldc.fr>
Date: Fri Apr 9 14:15:17 2021 +0200
placeholder add tag
commit a93d542a6a294bf214a952236bc3cd708b019241
Author: Olivier Legat <olivier.legat@ldc.fr>
Date: Fri Apr 9 14:11:39 2021 +0200
résistance de l'ajour de tags au accents, majuscules, etc..
commit c5eb6296e9ae2fff2fb7e15a93349bfc965cac53
Author: Olivier Legat <olivier.legat@ldc.fr>
Date: Fri Apr 9 13:34:59 2021 +0200
BO tags sur produits
This commit is contained in:
parent
d7dc16fd07
commit
3bd65ee2f0
15 changed files with 1840 additions and 17 deletions
|
|
@ -7,6 +7,7 @@ use App\Models\OldSlug;
|
|||
use App\Models\FoodProduct;
|
||||
use App\Models\Product;
|
||||
use App\Models\Range;
|
||||
use App\Models\Tag;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
|
|
@ -141,7 +142,9 @@ class ProductsController extends Controller
|
|||
|
||||
|
||||
$ranges = Range::all();
|
||||
return view('bo.products.edit', compact('product', 'ranges'));
|
||||
$existingTags = Tag::all();
|
||||
$selectedtags = $product->tags;
|
||||
return view('bo.products.edit', compact('product', 'ranges','existingTags','selectedtags'));
|
||||
}
|
||||
|
||||
public function update(Request $request, $id)
|
||||
|
|
@ -161,6 +164,7 @@ class ProductsController extends Controller
|
|||
$star = $request->star === null ? 0 : 1;
|
||||
$slug = Str::slug($name);
|
||||
$rangeId = $request->range_id;
|
||||
$tags = $request->tags;
|
||||
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
|
|
@ -246,9 +250,37 @@ class ProductsController extends Controller
|
|||
$this->$methodName($request, $product->id);
|
||||
}
|
||||
|
||||
$arTagIds = array();
|
||||
foreach($tags as $tag) {
|
||||
$oTag = json_decode($tag);
|
||||
if(empty($oTag->id)) {
|
||||
//we need a new tag
|
||||
$arTagIds[] = $this->storeTag($oTag);
|
||||
} else {
|
||||
$arTagIds[] = $oTag->id;
|
||||
}
|
||||
|
||||
}
|
||||
//prise en charge de la liaison avec les produits
|
||||
$product->tags()->sync($arTagIds);
|
||||
|
||||
return redirect()->route('bo.products.edit', $product->id)->with('success', 'Modifications enregistrés !');
|
||||
}
|
||||
|
||||
public function storeTag($oTag)
|
||||
{
|
||||
$slug = Str::slug($oTag->label);
|
||||
$tag = Tag::where('slug', $slug)->first();
|
||||
if($tag === null) {
|
||||
//si on pas encore ce tag on le crée
|
||||
$tag = new Tag();
|
||||
$tag->label = $oTag->label;
|
||||
$tag->slug = Str::slug($oTag->label);
|
||||
$tag->save();
|
||||
}
|
||||
return $tag->id;
|
||||
}
|
||||
|
||||
public function destroy($id)
|
||||
{
|
||||
$product = Product::findOrFail($id);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ namespace App\Http\Controllers;
|
|||
use App\Models\Product;
|
||||
use App\Models\Range;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ProductsController extends Controller
|
||||
{
|
||||
|
|
@ -40,7 +41,24 @@ class ProductsController extends Controller
|
|||
|
||||
public function allProducts(Request $request) {
|
||||
//do better
|
||||
$products = Product::where('status', 1)->paginate(12);
|
||||
return view ('search', compact('products'));
|
||||
|
||||
$filter = $request->query('filter');
|
||||
$filtertag = $request->query('tag');
|
||||
if (!empty($filter)) {
|
||||
$products = Product::where('status', 1)
|
||||
->where('name', 'like', '%'.$filter.'%')
|
||||
->paginate(12);
|
||||
} elseif (!empty($filtertag)) {
|
||||
$products = Product::where('status', 1)
|
||||
->whereHas('tags', function($q) use($filtertag)
|
||||
{
|
||||
$q->where('slug', '=', Str::slug($filtertag));
|
||||
})
|
||||
->paginate(12);
|
||||
} else {
|
||||
$products = Product::where('status', 1)->paginate(12);
|
||||
}
|
||||
|
||||
return view ('search', compact('products', 'filter', 'filtertag'));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -264,4 +264,10 @@ class Product extends Model implements Buyable
|
|||
{
|
||||
return $this->belongsTo(TaxRate::class, 'tax_rate_id');
|
||||
}
|
||||
|
||||
//tags
|
||||
public function tags()
|
||||
{
|
||||
return $this->belongsToMany(Tag::class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
18
app/Models/Tag.php
Normal file
18
app/Models/Tag.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Tag extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
public function products()
|
||||
{
|
||||
return $this->belongsToMany(Product::class);
|
||||
}
|
||||
}
|
||||
37
database/migrations/2021_04_09_081311_create_tags_table.php
Normal file
37
database/migrations/2021_04_09_081311_create_tags_table.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
use Database\Seeders\TagsSeeder;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateTagsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('tags', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('label');
|
||||
$table->string('slug');
|
||||
$table->collation = 'utf8mb4_unicode_ci';
|
||||
});
|
||||
|
||||
$seeder = new TagsSeeder();
|
||||
$seeder->run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('tags');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateProductTagTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('product_tag', function (Blueprint $table) {
|
||||
$table->foreignId('product_id')->constrained('products'); //produit considéré
|
||||
$table->foreignId('tag_id')->constrained('tags'); //tag associée
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('product_tag');
|
||||
}
|
||||
}
|
||||
58
database/seeders/TagsSeeder.php
Normal file
58
database/seeders/TagsSeeder.php
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Tag;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class TagsSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$canard = new Tag();
|
||||
$canard->label = "Canard";
|
||||
$canard->slug = Str::slug($canard->label);
|
||||
$canard->save();
|
||||
|
||||
$poulet = new Tag();
|
||||
$poulet->label = "Poulet";
|
||||
$poulet->slug = Str::slug($poulet->label);
|
||||
$poulet->save();
|
||||
|
||||
$dinde = new Tag();
|
||||
$dinde->label = "Dinde";
|
||||
$dinde->slug = Str::slug($dinde->label);
|
||||
$dinde->save();
|
||||
|
||||
$charcuterie = new Tag();
|
||||
$charcuterie->label = "Charcuteries";
|
||||
$charcuterie->slug = Str::slug($charcuterie->label);
|
||||
$charcuterie->save();
|
||||
|
||||
$panes = new Tag();
|
||||
$panes->label = "Panés";
|
||||
$panes->slug = Str::slug($panes->label);
|
||||
$panes->save();
|
||||
|
||||
$dinde = new Tag();
|
||||
$dinde->label = "Dinde";
|
||||
$dinde->slug = Str::slug($dinde->label);
|
||||
$dinde->save();
|
||||
|
||||
$produits_surgeles = new Tag();
|
||||
$produits_surgeles->label = "Produits surgelés";
|
||||
$produits_surgeles->slug = Str::slug($produits_surgeles->label);
|
||||
$produits_surgeles->save();
|
||||
|
||||
$paniers = new Tag();
|
||||
$paniers->label = "Paniers garnis";
|
||||
$paniers->slug = Str::slug($paniers->label);
|
||||
$paniers->save();
|
||||
}
|
||||
}
|
||||
6
package-lock.json
generated
6
package-lock.json
generated
|
|
@ -1124,6 +1124,12 @@
|
|||
"integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==",
|
||||
"dev": true
|
||||
},
|
||||
"@voerro/vue-tagsinput": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@voerro/vue-tagsinput/-/vue-tagsinput-2.7.1.tgz",
|
||||
"integrity": "sha512-G7ClILHgtc/D1vEw9qp6dlsAstlhwLDkPvEp6w1iY/dmSzu4hhxaMk8jyahjxQRDgiLlrAikESOsfkVhy18YwQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@vue/component-compiler-utils": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.2.0.tgz",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --config=node_modules/laravel-mix/setup/webpack.config.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@voerro/vue-tagsinput": "^2.7.1",
|
||||
"axios": "^0.21.1",
|
||||
"bootstrap": "^4.6.0",
|
||||
"cross-env": "^7.0",
|
||||
|
|
|
|||
1534
public/b-o/js/app.js
vendored
1534
public/b-o/js/app.js
vendored
File diff suppressed because one or more lines are too long
3
resources/js/app.js
vendored
3
resources/js/app.js
vendored
|
|
@ -5,9 +5,12 @@ window.Vue = require('vue');
|
|||
// import Vuex from 'vuex';
|
||||
// import { Container, Draggable } from 'vue-smooth-dnd';
|
||||
// import { applyDrag, generateItems } from './helpers';
|
||||
import VoerroTagsInput from '@voerro/vue-tagsinput';
|
||||
|
||||
Vue.component('faq', require('./components/Faq.vue').default);
|
||||
Vue.component('products-cards-dnd', require('./components/ProductsCardsDnd.vue').default);
|
||||
Vue.component('tags-input', VoerroTagsInput);
|
||||
Vue.component('auto-complete-tags', require('./components/AutoCompleteTags.vue').default);
|
||||
|
||||
const app = new Vue({
|
||||
el: '#app',
|
||||
|
|
|
|||
43
resources/js/components/AutocompleteTags.vue
Normal file
43
resources/js/components/AutocompleteTags.vue
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<template>
|
||||
<tags-input element-id="tags"
|
||||
v-model="selectedTags"
|
||||
:existing-tags="existingtags"
|
||||
id-field="id"
|
||||
text-field="label"
|
||||
placeholder="Ajouter un tag"
|
||||
:typeahead="true"
|
||||
typeahead-style="dropdown">
|
||||
</tags-input>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default ({
|
||||
components: { VoerroTagsInput },
|
||||
props: {
|
||||
dbexistingtags: {
|
||||
type: String,
|
||||
},
|
||||
dbselectedtags: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedTags: [],
|
||||
existingtags: [],
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
console.log('Autocomplete Tags Component mounted.');
|
||||
this.existingtags = JSON.parse(this.dbexistingtags);
|
||||
this.selectedTags = JSON.parse(this.dbselectedtags);
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import url("https://cdn.jsdelivr.net/npm/@voerro/vue-tagsinput@2.7.1/dist/style.css");
|
||||
</style>
|
||||
|
||||
|
|
@ -26,6 +26,10 @@
|
|||
<label for="name">Nom du produit *</label>
|
||||
<input type="text" id="name" name="name" class="form-control" placeholder="Nom du produit" value="{{$product->name}}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="name">Tags</label>
|
||||
<auto-complete-tags dbexistingtags='@json($existingTags)' dbselectedtags='@json($selectedtags)'></auto-complete-tags>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Description *</label>
|
||||
<textarea class="form-control" id="description" name="description" placeholder="Description *">{{$product->description}}</textarea>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,21 @@
|
|||
@section('title', __('Tous les produits'))
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="auth-form auth-form_register">
|
||||
<form class="auth-form__form form" method="GET">
|
||||
<div class="form__row">
|
||||
<div class="form__field">
|
||||
<input type="text" class="form__input" id="filter" name="filter" placeholder="Nom du produit" value="{{$filter}}">
|
||||
</div>
|
||||
<div class="form__field">
|
||||
<input type="text" class="form__input" id="tag" name="tag" placeholder="Tag" value="{{$filtertag}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form__field form__submit">
|
||||
<button type="submit" class="form__btn btn btn_blue">{{ __('Filtrer') }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@include('partials.products-list')
|
||||
|
||||
@include('partials.ranges-slider')
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
</li>
|
||||
</ul>
|
||||
|
||||
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
|
||||
{{--<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
|
||||
<span>PAGES</span>
|
||||
</h6>
|
||||
<ul class="nav flex-column mb-2">
|
||||
|
|
@ -59,9 +59,31 @@
|
|||
<a class="nav-link" href="{{route('bo.products.index')}}">Produits</a>
|
||||
<a class="nav-link" href="{{route('bo.ranges.index')}}">Gammes de produits</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="accordion" id="accordionExample">
|
||||
{{-- <div class="card">
|
||||
</ul>--}}
|
||||
<div class="accordion" id="accordionMenu">
|
||||
<div class="card">
|
||||
<div class="card-header" id="headingPages">
|
||||
<h2 class="mb-0">
|
||||
<button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse" data-target="#collapsePages" aria-expanded="true" aria-controls="collapseOne">
|
||||
Pages
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div id="collapsePages" class="collapse {{ ( request()->routeIs('bo.content') || request()->routeIs('bo.faq') ) ? 'show' : '' }}" aria-labelledby="headingPages" tata-parent="#accordionMenu">
|
||||
<div class="card-body">
|
||||
<ul class="nav flex-column mb-2">
|
||||
<li class="nav-item"><a class="nav-link" href="{{route('bo.content', 'accueil')}}">Accueil</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{route('bo.content', 'contact')}}">Contact</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{route('bo.content', 'legals')}}">Mentions Légales</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{route('bo.content', 'cgv')}}">CGV</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{route('bo.content', 'confidentialite')}}">Politique de confidentialité</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="{{route('bo.faq')}}">FAQ</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header" id="headingProduits">
|
||||
<h2 class="mb-0">
|
||||
<button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse" data-target="#collapseProduits" aria-expanded="true" aria-controls="collapseOne">
|
||||
|
|
@ -70,17 +92,17 @@
|
|||
</h2>
|
||||
</div>
|
||||
|
||||
<div id="collapseProduits" class="collapse" aria-labelledby="headingProduits" tata-parent="#accordionExample">
|
||||
<div id="collapseProduits" class="collapse {{ ( request()->routeIs('bo.products*') || request()->routeIs('bo.ranges*') ) ? 'show' : '' }}" aria-labelledby="headingProduits" tata-parent="#accordionMenu">
|
||||
<div class="card-body">
|
||||
<ul class="nav flex-column mb-2">
|
||||
<li class="nav-item">
|
||||
<ul class="nav flex-column mb-2">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{route('bo.products.index')}}">Produits</a>
|
||||
<a class="nav-link" href="{{route('bo.ranges.index')}}">Gammes de produits</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div> --}}
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header" id="headingMerchandising">
|
||||
<h2 class="mb-0">
|
||||
|
|
@ -90,7 +112,7 @@
|
|||
</h2>
|
||||
</div>
|
||||
|
||||
<div id="collapseMerchandising" class="collapse {{ ( request()->routeIs('bo.promotions*') || request()->routeIs('bo.products.starred') ) ? 'show' : '' }}" aria-labelledby="headingMerchandising" tata-parent="#accordionExample">
|
||||
<div id="collapseMerchandising" class="collapse {{ ( request()->routeIs('bo.promotions*') || request()->routeIs('bo.products.starred') ) ? 'show' : '' }}" aria-labelledby="headingMerchandising" tata-parent="#accordionMenu">
|
||||
<div class="card-body">
|
||||
<ul class="nav flex-column mb-2">
|
||||
<li class="nav-item">
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue