Besoin d'aide ?

Comment construire un query filter

  • Avatar de ThorOddin'sSon
    Membre depuis :
    31/01/2018
    Messages :
    98

    Bonjour,

    J'ai beau faire des recherches, mais je ne trouves rien pour comprendre comment construire un query filter, soit que s'est pour une version antérieur de Laravel 5.5 ou bien, il ne détail pas suffisament quel fichier modifier ou créer et quoi y inscrire.

    Quelqu'un serait en mesure de me donner un tutoriel explicite que je n'aurais pas trouvé?

    J'essais de faire en sorte de modifier l'affichage d'une page où il me serait utile de mettre en place un filtre permettant d'alléger la recherche d'élément.
    Imgur

    Le même genre de système exploité par les boutiques en ligne qui permet de sélectionner, je suis conscient que j'aurais du JavaScript à inscrire, mais j'ai besoin de cela énivitablement.

  • Avatar de Farris27
    Membre depuis :
    31/10/2017
    Messages :
    65

    Un exemple que j'ai moi même concut :

    Niveau controller :

    public function recherche(Request $request){

    $donnee= $request->donnee;

    $champ= $request->champ;

    $nombre = $request->nombre;

    $Produit = Sous_produits::whereHas('owner1', function ($query) {
    $query->where([['categorie_id', '=', 1],['visible','=',1]]);

    });

    $sousProduit = $Produit->newQuery();

    for ($i = 0; $i <= $nombre-1; $i++) {
    if (!empty($donnee[$i]) && $donnee[$i] !=='' ) {
    $sousProduit->where($champ[$i], 'like', "%".$donnee[$i]."%");
    }
    }

    //dd($i);
    // Get the results and return them.
    $sousProduit=$sousProduit->get();

    return \Illuminate\Support\Facades\Response::json([
    'sousProduit'=> $sousProduit
    ]);

    }

    niveau front:

    $('.blabla').on('keyup change',function (){

    // Enregistrement dans un tableau
    var selection = $(".blabla[type='text']");
    var champ={};
    var donnee={};
    var nombre =0;

    // On parcourt soit avec each :
    selection.each(function (n) {
    champ[n] = $(this).attr('data-type');
    donnee[n] = $(this).val();
    nombre =n+1;

    });

    console.log(champ,donnee,nombre);
    console.log('blabla avant post');

    $.ajax({

    url: '{{route('filtrage')}}',
    method:'POST',
    data:
    {
    donnee : donnee,
    champ : champ,
    nombre: nombre
    },
    dataType: 'json',
    success: function (data) {

    console.log(data);
    $('#sousProduit').empty();
    console.log('test'+data.sousProduit);
    if(data.sousProduit ==''){

    var code = '<tr>'
    +'<td>'
    +'<a href="{{route('mesure')}}">'+' faire une pièce sur mesure '+' </a>'
    +'</td>'
    +'<tr>';

    $('#sousProduit').append(code);

    } else{

    for(var i= 0; i < data.sousProduit.length; i++){

    var code = '<tr>'
    +'<td>'
    +'<a href="#">'+' voir les détails '+' </a>'
    +'</td>'

    +'<td>'
    +'<div>'+ ( typeof data.sousProduit[i].reference !== 'object' ? data.sousProduit[i].reference : "" ) +'</div>'
    +'</td>'

    +'<td >'
    +'<div>'+ ( typeof data.sousProduit[i].longueur !== 'object' ? data.sousProduit[i].longueur : "" ) +'</div>'
    +'</td>'

    +'<td >'
    +'<div>'+ ( typeof data.sousProduit[i].profil_1_dimensions_A !== 'object' ? data.sousProduit[i].profil_1_dimensions_A : "" ) +'</div>'
    +'</td>'

    +'<td >'
    +'<div>'+ ( typeof data.sousProduit[i].profil_1_dimensions_B !== 'object' ? data.sousProduit[i].profil_1_dimensions_B : "" ) +'</div>'
    +'</td>'

    +'<td >'
    +'<div>'+ ( typeof data.sousProduit[i].profil1_mat !== 'object' ? data.sousProduit[i].profil1_mat : "" ) +'</div>'
    +'</td>'

    +'<td >'
    +'<div>'+ ( typeof data.sousProduit[i].axe_dimensions_A !== 'object' ? data.sousProduit[i].axe_dimensions_A : "" ) +'</div>'
    +'</td>'

    +'<td >'
    +'<div>'+ ( typeof data.sousProduit[i].axe_dimensions_B !== 'object' ? data.sousProduit[i].axe_dimensions_B : "" ) +'</div>'
    +'</td>'
    +'<td >'
    +'<div>'+ ( typeof data.sousProduit[i].axe_mat !== 'object' ? data.sousProduit[i].axe_mat : "" ) +'</div>'
    +'</td>'

    +'<td >'
    +'<div>'+ ( typeof data.sousProduit[i].rondelle_diametre_exterieur !== 'object' ? data.sousProduit[i].rondelle_diametre_exterieur : "" ) +'</div>'
    +'</td>'

    +'<td >'
    +'<div>'+ ( typeof data.sousProduit[i].rondelle_diametre_interieur !== 'object' ? data.sousProduit[i].rondelle_diametre_interieur : "" ) +'</div>'
    +'</td>'

    +'<td >'
    +'<div>'+ ( typeof data.sousProduit[i].rondelle_epaisseur !== 'object' ? data.sousProduit[i].rondelle_epaisseur : "" ) +'</div>'
    +'</td>'

    +'<td >'
    +'<div>'+ ( typeof data.sousProduit[i].rondelle_mat !== 'object' ? data.sousProduit[i].rondelle_mat : "" ) +'</div>'
    +'</td>'

    @if(count($accessoires)>=1)
    +'<td >'
    +'<div>'+ ( typeof data.sousProduit[i].mat1 !== 'object' ? data.sousProduit[i].mat1 : "" ) +'</div>'
    +'</td>'
    @endif

    @if(count($accessoires)>=2)
    +'<td >'
    +'<div>'+ ( typeof data.sousProduit[i].mat2 !== 'object' ? data.sousProduit[i].mat2 : "" ) +'</div>'
    +'</td>'
    @endif

    @if(count($accessoires)>=3)
    +'<td >'
    +'<div>'+ ( typeof data.sousProduit[i].mat3 !== 'object' ? data.sousProduit[i].mat3 : "" ) +'</div>'
    +'</td>'
    @endif

    @if(count($accessoires)>=4)

    +'<td >'
    +'<div>'+ ( typeof data.sousProduit[i].mat4 !== 'object' ? data.sousProduit[i].mat4 : "" ) +'</div>'
    +'</td>'

    @endif

    +'<td >'
    +'<a data-toggle="modal" data-target="#myModal'+data.sousProduit[i].id+'">'+' Ajouter au panier'+'</a>'
    +'</td>'

    +'</tr>';

    //On ajoute chaque participant à la table
    $('#sousProduit').append(code);

    } }

    },
    //Sinon
    error: function (data) {
    swal({
    title: 'Cela est introuvable !',
    type: 'error',
    timer: 2000,
    showConfirmButton: false

    });
    }
    })

    à toi de modifier ça avec des checkbox ;)

  • Avatar de ThorOddin'sSon
    Membre depuis :
    31/01/2018
    Messages :
    98

    Hum... je suis pas sur de comprendre ton script pour le contrôleur et l'adapter pour le mien, je vois bien que tu as trois valeurs nommée $donnee, $champ, $nombre, mais tu ne les places pas dans le whereHas, tandis que moi, j'ai besoin de spécifier l'élément recherché.

    J'avais trouver ce tutoriel, mais il modifie tellement de chose d'étape en étape, que je me suis perdu dans les étapes. Je trouve qu'il passe du coq à l'âne pour revenir au cochon dans ses explications.
    Writing advanced Eloquent search query filters

  • Avatar de ThorOddin'sSon
    Membre depuis :
    31/01/2018
    Messages :
    98

    J'en suis rendu là pour l'instant :

    Le contrôleur que j'ai créé en suivant le tutoriel.

    <?php

    namespace App\ExercisesSearch;

    use Illuminate\Http\Request;
    use App\Models\Exercises;
    use Illuminate\Database\Eloquent\Builder;

    class ExercisesSearch
    {
    public static function apply(Request $filters)
    {
    $query =
    static::applyDecoratorsFromRequest(
    $filters, (new User)->newQuery()
    );

    return static::getResults($query);
    }

    private static function applyDecoratorsFromRequest(Request $request, Builder $query)
    {
    foreach ($request->all() as $filterName => $value) {

    $decorator = static::createFilterDecorator($filterName);

    if (static::isValidDecorator($decorator)) {
    $query = $decorator::apply($query, $value);
    }

    }
    return $query;
    }

    private static function createFilterDecorator($name)
    {
    return return NAMESPACE . '\Filters\' .
    str_replace(' ', '',
    ucwords(strreplace('', ' ', $name)));
    }

    private static function isValidDecorator($decorator)
    {
    return class_exists($decorator);
    }

    private static function getResults(Builder $query)
    {
    return $query->get();
    }
    }

    Le filtre :

    <?php

    namespace App\ExercisesSearch\Filters;

    use Illuminate\Database\Eloquent\Builder;

    interface Filter
    {
    /*
    Apply a given search value to the builder instance.

    @param Builder $builder
    @param mixed $value
    @return Builder $builder
    */
    public static function apply(Builder $builder, $value);
    }

    Les filtres pour chacun des éléments recherché :

    <?php

    namespace App\ExercisesSearch\Filters;

    use Illuminate\Database\Eloquent\Builder;

    class Equipments implements Filter
    {
    /*
    Apply a given search value to the builder instance.

    @param Builder $builder
    @param mixed $value
    @return Builder $builder
    */
    public static function apply($builder, $value)
    {
    return $builder->where('equipment_id', $value);
    }
    }
    <?php

    namespace App\ExercisesSearch\Filters;

    use Illuminate\Database\Eloquent\Builder;

    class Muscles implements Filter
    {
    /*
    Apply a given search value to the builder instance.

    @param Builder $builder
    @param mixed $value
    @return Builder $builder
    */
    public static function apply($builder, $value)
    {
    return $builder->where('muscle_id', $value);
    }
    }
    <?php

    namespace App\ExercisesSearch\Filters;

    use Illuminate\Database\Eloquent\Builder;

    class Types implements Filter
    {
    /*
    Apply a given search value to the builder instance.

    @param Builder $builder
    @param mixed $value
    @return Builder $builder
    */
    public static function apply($builder, $value)
    {
    return $builder->where('type_id', $value);
    }
    }

    Le fichier JavaScript que je tente de créer, mais qui ne veut pas fonctionner :

    $(document).ready(function () {
    var exercises = [];

    $('.form-check-input').click(function(){
    setTimeout(function() {
    $('input[class="form-check-input"]:checked').each(function(){
    exercises.push($(this).val())

    });

    $.post('/search', $('.form-check-input').serialize(), function(){
    setTimeout(function() {
    $('#exercise').hide();
    var exercises = $.parseJSON();
    if (exercises['mediatype'] == 0){
    var data = ('<div id="exercises" class="card"><div class="card-header"><h5 class="card-title"><a class="text-dark" href="exercise/'+exercises['id']+'">'+exercises['name']+'</a></h5></div><div class="card-body"><div class="row"><div class="media"><img class="align-self-center mr-3" src="/images/exercises/'+exercises['mainmedia']+'" width="240" height="240"><div class="media-body"><div class="mt-0 row"><p>Équipement :&nbsp;</p>'+exercises['equipment['name']']+'</div><div class="mt-0 row"><p>Type :&nbsp;</p>'+exercises['type['name']']+'</div><div class="mt-0 row"><p>Muscle principal :&nbsp;</p>'+exercises['muscle['name']']+'</div></div></div></div></div></div>');
    }else{
    var data = ('<div id="exercises" class="card"><div class="card-header"><h5 class="card-title"><a class="text-dark" href="exercise/'+exercises['id']+'">'+exercises['name']+'</a></h5></div><div class="card-body"><div class="row"><div class="media"><video class="align-self-center mr-3" width="320" height="240" controls><source src="/video/'+exercises['mainmedia']+'" type="video/mp4"></video><div class="media-body"><div class="mt-0 row"><p>Équipement :&nbsp;</p>'+exercises['equipment['name']']+'</div><div class="mt-0 row"><p>Type :&nbsp;</p>'+exercises['type['name']']+'</div><div class="mt-0 row"><p>Muscle principal :&nbsp;</p>'+exercises['muscle['name']']+'</div></div></div></div></div></div>');
    }
    $('data').insertAfter('#exercise');

    });
    });
    });
    });
    });
  • Avatar de ThorOddin'sSon
    Membre depuis :
    31/01/2018
    Messages :
    98

    Je coince au niveau de mon JavaScript. J'ai un message d'erreur au niveau de la première ligne var data =...

  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    2079

    Salut,

    C'est sûr que ça fait un peu confus, je pense que ça doit mieux passer comme ça :

    var data = '<div id="exercises" class="card"><div class="card-header"><h5 class="card-title"><a class="text-dark" href="'
    +'exercise/' + exercises['id']
    +'">'
    +exercises['name']
    +'</a></h5></div><div class="card-body"><div class="row"><div class="media"><img class="align-self-center mr-3" src="'
    +'/images/exercises/' + exercises['mainmedia']
    +'" width="240" height="240"><div class="media-body"><div class="mt-0 row"><p>Équipement :&nbsp;</p>'
    +exercises[equipment['name']]
    +'</div><div class="mt-0 row"><p>Type :&nbsp;</p>'
    +exercises[type['name']]
    +'</div><div class="mt-0 row"><p>Muscle principal :&nbsp;</p>'
    +exercises[muscle['name']]
    +'</div></div></div></div></div></div>';
  • Avatar de ThorOddin'sSon
    Membre depuis :
    31/01/2018
    Messages :
    98

    Bon, l'erreur n'est plus la même maintenant : Uncaught TypeError: $.post is not a function.

  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    2079

    Tu as mis quoi comme version de JQuery ?

  • Avatar de ThorOddin'sSon
    Membre depuis :
    31/01/2018
    Messages :
    98

    jquery-3.2.1.slim.min.js pour Bootstrap 4

  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    2079

    Mets la version complète, pas la slim.

  • Avatar de ThorOddin'sSon
    Membre depuis :
    31/01/2018
    Messages :
    98

    ok, bon, j'ai une amélioration, j'ai un message d'erreur un peu plus détaillé :

    POST http://localhost:8000/search 419 (unknown status)
    send @ jquery.min.js:2
    ajax @ jquery.min.js:2
    w.(anonymous function) @ jquery.min.js:2
    (anonymous) @ exercises:91
    setTimeout (async)
    (anonymous) @ exercises:85
    dispatch @ jquery.min.js:2
    y.handle @ jquery.min.js:2
  • Avatar de ThorOddin'sSon
    Membre depuis :
    31/01/2018
    Messages :
    98

    Nouvelle amélioration, je suis passé de l'erreur 419 à la 500 :

    jquery.min.js:2 POST http://localhost:8000/search 500 (Internal Server Error)
    send @ jquery.min.js:2
    ajax @ jquery.min.js:2
    w.(anonymous function) @ jquery.min.js:2
    (anonymous) @ exercises:101
    setTimeout (async)
    (anonymous) @ exercises:95
    dispatch @ jquery.min.js:2
    y.handle @ jquery.min.js:2

    J'ai découvert que l'erreur 419 était causé par le fait que le certificat n'était pas transmit, j'ai ajouter ces lignes dans mon script :

    $.ajaxSetup({

    headers: {

    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')

    }

    });
  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    2079

    Une erreur 500 ça veut dire que ça se passe côté PHP, regarde le contenu de l'erreur.

  • Avatar de ThorOddin'sSon
    Membre depuis :
    31/01/2018
    Messages :
    98

    D'accord!

    Le problème viendrait de mon contrôleur.

    Call to undefined method App\Http\Controllers\SearchController::applyFiltersToQuery()
    /Applications/MAMP/htdocs/leveluplifev0.2/app/Http/Controllers/SearchController.php#20
    Symfony\Component\Debug\Exception\FatalThrowableError

    class SearchController extends Controller
    {
    public static function filter(Request $filters)
    {
    $query = (new Exercises)->newQuery();

    $query = static::applyFiltersToQuery($filters, $query);

    return $query->with('Equipment:id,name', 'Type:id,name', 'Muscle:id,name')->get();
    }
    }
  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    2079

    Elle est où cette méthode applyFiltersToQuery ?

  • Avatar de ThorOddin'sSon
    Membre depuis :
    31/01/2018
    Messages :
    98

    J'ai suivis se tutoriel là pour parvenir à faire mon filtre : https://m.dotdev.co/writing-advanced-eloquent-search-query-filters-de8b6c2598db

    J'ai aucune idée d'où il peut venir le applyFiltersToQuery, s'est le seul tutoriel que je suis parvenus à trouver et qui me semblait correspondre à mes besoins.

    J'ai renommé selon se que devait être dans le tutoriel initial :

    <?php

    namespace App\Http\Controllers;

    use App\ExercisesSearch\ExercisesSearch;
    use App\Http\Requests;
    use Illuminate\Http\Request;
    use App\Http\Controllers\Controller;
    use App\Models\Exercises;
    use App\Models\Types;
    use App\Models\Muscles;
    use App\Models\Equipments;

    class SearchController extends Controller
    {
    public static function filter(Request $filters)
    {
    $query = (new Exercises)->newQuery();

    $query = static::applyDecoratorsFromRequest($filters, $query);

    return $query->with('Equipment:id,name', 'Type:id,name', 'Muscle:id,name')->get();
    }
    }
    <?php

    namespace App\ExercisesSearch;

    use Illuminate\Http\Request;
    use App\Models\Exercises;
    use Illuminate\Database\Eloquent\Builder;

    class ExercisesSearch
    {
    public static function apply(Request $filters)
    {
    $query =
    static::applyDecoratorsFromRequest(
    $filters, (new User)->newQuery()
    );

    return static::getResults($query);
    }

    private static function applyDecoratorsFromRequest(Request $filters, Builder $query)
    {
    foreach ($filters->all() as $filterName => $value) {

    $decorator = static::createFilterDecorator($filterName);

    if (static::isValidDecorator($decorator)) {
    $query = $decorator::apply($query, $value);
    }

    }
    return $query;
    }

    Malgré la concordance, j'ai toujours le même message d'erreur.

  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    2079

    J'ai lu rapidement l'article, pour la version finale il a retiré cette méthode :

    I decided to remove the applyFiltersToQuery() method as it seemed redundant considering the main method is called ‘apply’.

    J'ai regardé le code sur Github ça paraît très propre.

    Dans mon exemple j'ai adopté une autre démarche. Comme j'utilise les routines pour plusieurs contrôleurs j'ai créé un trait Indexable :

    <?php

    namespace App\Http\Controllers\Back;

    use Illuminate\Http\Request;
    use App\Services\Thumb;

    trait Indexable
    {
    /
    The PostRepository instance.

    @var \App\Repositories\PostRepository
    /
    protected $repository;

    /

    The table.

    @var string
    /
    protected $table;

    /*
    Display a listing of the records.

    @param \Illuminate\Http\Request $request
    @return \Illuminate\Http\Response
    /
    public function index(Request $request)
    {
    $parameters = $this->getParameters ($request);

    // Get records and generate links for pagination
    $records = $this->repository->getAll (config ("app.nbrPages.back.$this->table"), $parameters);
    $links = $records->appends ($parameters)->links ('back.pagination');

    // Ajax response
    if ($request->ajax ()) {
    return response ()->json ([
    'table' => view ("back.$this->table.table", [$this->table => $records])->render (),
    'pagination' => $links->toHtml (),
    ]);
    }

    return view ("back.$this->table.index", [$this->table => $records, 'links' => $links]);
    }

    /*
    Get parameters.

    @param \Illuminate\Http\Request $request
    @return array
    /
    protected function getParameters($request)
    {
    // Default parameters
    $parameters = config("parameters.$this->table");

    // Build parameters with request
    foreach ($parameters as $parameter => &$value) {
    if (isset($request->$parameter)) {
    $value = $request->$parameter;
    }
    }

    return $parameters;
    }
    }

    Je crée un tableau avec les paramètres valides.
    Ensuite dans le repository j'ai juste à coder la méthode getAll, par exemple ici pour les posts :

    public function getAll($nbrPages, $parameters)
    {
    return $this->model->with ('ingoing')
    ->orderBy ($parameters['order'], $parameters['direction'])
    ->when ($parameters['active'], function ($query) {
    $query->whereActive (true);
    })->when ($parameters['new'], function ($query) {
    $query->has ('ingoing');
    })->when (auth()->user()->role === 'redac', function ($query) {
    $query->whereHas('user', function ($query) {
    $query->where('users.id', auth()->id());
    });
    })->paginate ($nbrPages);
    }
  • Avatar de ThorOddin'sSon
    Membre depuis :
    31/01/2018
    Messages :
    98

    Donc, je devrais tout reprendre mon code au complet et adapter ton code pour mes besoins où de revoir mon code en comparent avec le github du tutoriel?

  • Avatar de bestmomo
    Membre depuis :
    07/04/2013
    Messages :
    2079

    Bah tu as le choix :)

  • Avatar de ThorOddin'sSon
    Membre depuis :
    31/01/2018
    Messages :
    98

    J'ai revisité mes codes en faisant une comparaison entre mon code et le code du github.

    Je me retrouve avec le même genre d'erreur :

    Class 'App\ExercisesSearch\ExercisesSearch' not found
    /Applications/MAMP/htdocs/leveluplifev0.2/app/Http/Controllers/SearchController.php#18
    Symfony\Component\Debug\Exception\FatalThrowableError

    namespace App\Http\Controllers;

    use App\ExercisesSearch\ExercisesSearch;
    use App\Http\Requests;
    use Illuminate\Http\Request;
    use App\Http\Controllers\Controller;
    use App\Models\Exercises;
    use App\Models\Types;
    use App\Models\Muscles;
    use App\Models\Equipments;

    class SearchController extends Controller
    {
    public static function filter(Request $request)
    {
    return ExercisesSearch::apply($request);
    }
    }

    La class existe bien :

    namespace App\ExercisesSearch;

    use Illuminate\Http\Request;
    use App\Models\Exercises;
    use Illuminate\Database\Eloquent\Builder;

    class ExercisesSearch
    {
    public static function apply(Request $filters)
    {
    $query =
    static::applyDecoratorsFromRequest(
    $filters, (new Exercises)->newQuery()
    );

    return static::getResults($query);
    }

    Aurais-je mal écrit le namespace?

Vous ne pouvez pas répondre à ce sujet.