Wyszukiwarka użytkowników w Laravel

0

Witam serdecznie.
Zaczynam przygodę z Laravelem i natrafiłem na mały problem :(
Wykorzystuję w moim projekcie Laravel 5.8.

Mam działający kod wyświetlający listę użytkowników.

Mój kod wygląda następująco:

Users:



    class User extends Authenticatable implements MustVerifyEmail
    {
        use Notifiable;
        use psCMS\Presenters\UserPresenter;
    
        public static $roles = [];
    
        /**
         * The attributes that are mass assignable.
         *
         * @var array
         */
    
        protected $fillable = ['user_height', 'year_birth', 'company_id', 'enable', 'name', 'surname', 'email', 'email_verified_at', 'password', 'counter', 'url_address', 'isCompany', 'isMailing', 'content', 'nip1', 'business1', 'phone1', 'street1', 'number1', 'postal_code1', 'city1', 'country_id1', 'provincial_id1', 'nip2', 'business2', 'phone2', 'street2', 'number2', 'postal_code2', 'city2', 'country_id2', 'provincial_id2', 'nip3', 'business3', 'phone3', 'street3', 'number3', 'postal_code3', 'city3', 'country_id3', 'provincial_id3', 'cash', 'lng', 'lat', 'enable_map', 'remember_token', 'created_at', 'updated_at', 'last_login_at', 'last_login_ip'];
    
    
        /**
         * The attributes that should be hidden for arrays.
         *
         * @var array
         */
        protected $hidden = [
            'password', 'remember_token',
        ];
    
    
        public function roles()
        {
            return $this->belongsToMany('App\Role');
        }
    
        public function mainRole()
        {
            return $this->hasOne('App\Role');
        }
    
        public function comments()
        {
            return $this->hasMany('App\Comments');
        }
    
    
        public function hasRole(array $roles)
        {
    
            foreach ($roles as $role) {
    
                if (isset(self::$roles[$role])) {
                    if (self::$roles[$role]) return true;
    
                } else {
                    self::$roles[$role] = $this->roles()->where('name', $role)->exists();
                    if (self::$roles[$role]) return true;
                }
    
            }
            return false;
        }
    
        public function loginHistory()
        {
            return $this->hasMany('App\UserLoginHistory');
        }
    
        public function companies()
        {
            return $this->belongsTo('App\Companies', 'company_id');
        }
    
    
        public function images()
        {
            return $this->hasManyThrough('App\UploadedFiles', 'App\User', 'id', 'photoable_id');
        }
    
        public function AdminImage()
        {
            return $this->images()->where('photoable_type', '=', 'AdminImage');
        }
    
        public function scopeOfRoleType($query, $types)
        {
            return $query->whereHas('roles', function ($q) use ($types) {
                $q->whereIn('name', $types);
            });
        }
    
    }
    
    
    
    Schema::create('users', function (Blueprint $table) {
                $table->bigIncrements('id');
                $table->bigInteger('company_id')->unsigned();
                $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade');
                $table->boolean('enable')->default(0);
                $table->string('name', 120)->nullable();
                $table->string('surname', 120)->nullable();
                $table->string('email', 120)->unique();
                $table->timestamp('email_verified_at')->nullable();
                $table->string('password');
                $table->bigInteger('counter')->default(0);
                $table->string('url_address', 160);
                $table->boolean('isCompany')->default(0);
                $table->boolean('isMailing')->default(0);
                $table->text('content')->nullable();
                $table->string('nip1', 12)->nullable();
                $table->string('business1', 120)->nullable();
                $table->string('phone1', 60)->nullable();
                $table->string('street1', 150)->nullable();
                $table->string('number1', 8)->nullable();
                $table->string('postal_code1', 12)->nullable();
                $table->string('city1', 100)->nullable();
                $table->bigInteger('country_id1')->default(0);
                $table->bigInteger('provincial_id1')->default(0);
                $table->string('nip2', 12)->nullable();
                $table->string('business2', 120)->nullable();
                $table->string('phone2', 60)->nullable();
                $table->string('street2', 150)->nullable();
                $table->string('number2', 8)->nullable();
                $table->string('postal_code2', 12)->nullable();
                $table->string('city2', 100)->nullable();
                $table->bigInteger('country_id2')->default(0);
                $table->bigInteger('provincial_id2')->default(0);
                $table->string('nip3', 12)->nullable();
                $table->string('business3', 120)->nullable();
                $table->string('phone3', 60)->nullable();
                $table->string('street3', 150)->nullable();
                $table->string('number3', 8)->nullable();
                $table->string('postal_code3', 12)->nullable();
                $table->string('city3', 100)->nullable();
                $table->bigInteger('country_id3')->default(0);
                $table->bigInteger('provincial_id3')->default(0);
                $table->decimal('cash', 9, 2)->default(0);
                $table->decimal('lng', 10, 8)->default(0);
                $table->decimal('lat', 10, 8)->default(0);
                $table->boolean('enable_map')->default(0);
                $table->integer('year_birth')->default(date("Y"));
                $table->integer('user_height')->default(0);
                $table->rememberToken();
                $table->timestamps();
                $table->engine = "InnoDB";
            });

Services:


    class UsersServices extends Model
    {
        protected $quarded = [];
        protected $fillable = ['name', 'type', 'enable'];
        public $timestamps = false;
    }
    
    Schema::create('users_services', function (Blueprint $table) {
                $table->bigIncrements('id');
                $table->string('name', 100);
                $table->integer('type');
                $table->boolean('enable')->default(0);
                $table->engine = "InnoDB";
            });

WYbrane/świadczone przez użytkownika usługi:


    Schema::create('users_services_selected', function (Blueprint $table) {
                $table->bigInteger('user_id')->unsigned();
                $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
                $table->bigInteger('service_id')->unsigned();
                $table->foreign('service_id')->references('id')->on('users_services')->onDelete('cascade');
                $table->engine = "InnoDB";
            });
    
    class UserServicesSelected extends Model
    {
        protected $quarded = [];
        protected $fillable = ['user_id', 'service_id'];
        protected $table = 'users_services_selected';
        public $timestamps = false;
    }

Moja aktualna funkcja do wyświetlania użytkowników



    public function getUserList(string $query, string $sortColumn, string $sortMethod)
    {
        if ($query != "" || $sortMethod !="") {  echo "$sortColumn, $sortMethod";
            return User::ofRoleType(['user', 'userPremium', 'userSponsor'])
                ->where(function ($q) use ($query, $sortColumn, $sortMethod) {
                    $q->where('account_paid_for', '>=', date("Y-m-d"))
                        ->where('enable', '=', 1)
                        ->where('email_verified_at', '<>', null)
                        ->orderBy($sortColumn, $sortMethod);
                })->orderBy($sortColumn, $sortMethod)->paginate(15);
        } else {
            return User::ofRoleType(['user', 'userPremium', 'userSponsor'])
                ->where('account_paid_for', '>=', date("Y-m-d"))
                ->where('enable', '=', 1)
                ->where('email_verified_at', '<>', null)
                ->orderBy(DB::raw('IF(premium_for > CURDATE(), 0, 1)'))
                ->orderBy('hits', 'DESC')
                ->paginate(15);

        }
    }

Formatka do wyszukiwania:


    <form method="get" action="{{ route('result') }}">
    Wiek od <input type="text" name="s_year_birth_from" value=""></div>
    Wiek do <input type="text" name="s_year_birth_to" value=""></div>
    
    
    Wzrost użytkownika:
    Pokaż wszystko: <input type="checkbox" value="0" name="s_height[]">
    150 cm: <input type="checkbox" value="1" name="s_height[]">
    151-160 cm: <input type="checkbox" value="2" name="s_height[]">
    161-170 cm: <input type="checkbox" value="3" name="s_height[]">
    171-180 cm: <input type="checkbox" value="4" name="s_height[]">
    >180 cm: <input type="checkbox" value="5" name="s_height[]">
    
    
    Usługi:
    Pokaż wszystko: <input type="checkbox" value="0" name="s_services1[]">
    @foreach($services as $service)
    	{{ $service->name }} <input type="checkbox" value="{{ $service->id }}" name="s_services1[]"> 
    @endforeach
    
    <button type="submit">Szukaj</button>
    </form>

Wzrost użytkownika = User->user_height

Data urodzenia użytkownika = User->year_birth

Potrzebuję dorobić nowe opcje wyszukiwania:

  1. Wyświetlanie użytkowników posiadających odpowiedni wiek w oparciu o: s_year_birth_from i s_year_birth_to (User-> year_birth)

  2. Wyświetlanie użytkowników oferujących zaznaczone w wyszukiwarce usługi (zaznaczone / oferowane usługi = UserServicesSelected)

  3. Wyświetlanie użytkowników posiadających odpowiedni wzrost w oparciu o: s_height (User->user_height)

Jak można to zrobić? Chciałbym to dodać to do mojej obecnej funkcji wyświetlającej użytkowników: getUserList

Bardzo proszę o pomoc.

0
1. $query->whereBetween('nazwa_kolumny', request(['s_year_birth_from', 's_year_birth_to']));
2. $query->whereHas('relacja', function($query){
    //warunek
});
3. $query->where('nazwa_kolumny', request('s_height'));

Nie chciało mi się szukać nazw pól których używasz do konkretnych rzeczy, bo Twoja encja ma za dużo odpowiedzialności.

0
mefsh napisał(a):
1. $query->whereBetween('nazwa_kolumny', request(['s_year_birth_from', 's_year_birth_to']));
2. $query->whereHas('relacja', function($query){
    //warunek
});
3. $query->where('nazwa_kolumny', request('s_height'));

Nie chciało mi się szukać nazw pól których używasz do konkretnych rzeczy, bo Twoja encja ma za dużo odpowiedzialności.

Jeśli to głupie pytanie, to z góry przepraszam.

Mam ten kod:


return User::ofRoleType(['user', 'userPremium', 'userSponsor'])
                ->where(function ($q) use ($query, $sortColumn, $sortMethod) {
                    $q->where('account_paid_for', '>=', date("Y-m-d"))
                        ->where('enable', '=', 1)
                        ->where('email_verified_at', '<>', null)
                        ->orderBy($sortColumn, $sortMethod);
                })->orderBy($sortColumn, $sortMethod)->paginate(15);


Mógłbyś mi pokazać na przykładzie powyższego kodu jak to "poskładać"?
(zakładamy że nie wszystkie pola w formularzu wyszukiwania muszą być za każdym rezem wypełnione).

  1. Pole w formularzu data od: s_year_birth_from data do: s_year_birth_do
  2. Pole w formularzu usługa od usług: s_services1[]
  3. Pole w formularzu od wzrostu: s_height[]

Przepraszam za kłopot, ale dopiero uczę się Laravela i potrzebuję tego kodu pod kątek wzorca do nauki :)
Jeśli masz jakieś uwagi do mojego kodu, to możesz też napisać co jest do ewentualnej zmiany :)
To mój pierwszy framework ;)

0

Jeśli masz jakieś uwagi do mojego kodu, to możesz też napisać co jest do ewentualnej zmiany :)
To mój pierwszy framework ;)

Normalizacja bazy danych i programowanie obiektowe - te dwie rzeczy przede wszystkim, wtedy encja nie będzie miała 40 pól.

Co do przykładu, przypisz zapytanie do zmiennej i przenieś wywołanie go na później:

$query = User::ofRoleType(['user', 'userPremium', 'userSponsor'])
                ->where(function ($q) use ($query, $sortColumn, $sortMethod) {
                    $q->where('account_paid_for', '>=', date("Y-m-d"))
                        ->where('enable', '=', 1)
                        ->where('email_verified_at', '<>', null)
                        ->orderBy($sortColumn, $sortMethod);
                })->orderBy($sortColumn, $sortMethod);

//Dodatkowe warunki
if(!is_null(request('s_year_birth_from')) && !is_null(request('s_year_birth_from')){
    $query->whereBetween('nazwa_kolumny', request(['s_year_birth_from', 's_year_birth_to']));
}

$query->paginate(15);

W taki sposób dodajesz warunek jeżeli zostały przekazane jakieś dane.

0

Super, dziękuję bardzo :) Działa. :)

mam pytanie jeszcze do tych relacji.

Do User.php dodałem:

public function services()
    {
        return $this->hasMany('App\UserServicesSelected');
    }

i chciałbym teraz wyświetlić użytkowników wykonujących daną usługę (wyszukiwaną).

Dodaje coś takiego:


if (!is_null(request('s_services1'))) { 
            $query->whereHas('services', function($query){
                //warunek
            });
        }

tak? :)
Co miałeś na myśli pisząc o "warunku"?

0

Miałem na myśli warunek odnoszący się do relacji.
np.

$query->whereHas('services', function($query){
    $query->whereIn('id', [1, 2, 3])
});

Zwróci Ci tylko rekordy posiadające relację z services o id 1,2,3.

0
mefsh napisał(a):

Miałem na myśli warunek odnoszący się do relacji.
np.

$query->whereHas('services', function($query){
    $query->whereIn('id', [1, 2, 3])
});

Zwróci Ci tylko rekordy posiadające relację z services o id 1,2,3.

Dziękuję bardzo :) Jest mały problem. Jeśli użytkownik świadczy usługi A,B i C z puli usług A,B,C,D,E - zaznaczę że szukam usługi A,B,C,D,E - to użytkownik pomimo tego iż nie świadczy wszystkich szukanych usług - i tak zostanie wyświetlony na liście wyników (a nie powinien - nie świadczy wszystkich szukanych usług)

0

Mam jeszcze jedno pytanie - ostatnie już :)
Zrobiłem zgodnie z Twoją sugestią wyszukiwanie tego wzrostu. Jako iż można zaznaczyć więcej niż 1 szukaną pozycję to dodałem funkcję foreach:

if (!is_null(request('s_height'))) {
            foreach (request('s_height') as $i => $id) {
                if ($id != 0) {
                    if ($id == 1) {
                        $query->whereBetween('user_height', [0, 150]);
                    } elseif ($id == 2) {
                        $query->whereBetween('user_height', [151, 160]);
                    } elseif ($id == 3) {
                        $query->whereBetween('user_height', [161, 170]);
                    } elseif ($id == 4) {
                        $query->whereBetween('user_height', [171, 180]);
                    } elseif ($id == 5) {
                        $query->whereBetween('user_height', [181, 500]);
                    }
                }
            }
        }

Problem w tym że zwraca mi poprawne wyniki w momencie gdy zaznaczę szukany jeden wzrost (jeden przedział). Jak jest zaznaczony szukany więcej niż 1 przedział, to nie zwraca mi żadnego wyniku. Obstawiam że jest to wina błędnej konstrukcji zapytania przez ORM - daje coś w stylu ...

and `user_height` between 0 and 150 and `user_height` between 151 and 160 and `user_height` between 161 and 170 and `user_height` between 171 and 180 and `user_height` between 181 and 500

A jako iż użytkownik ma jeden wzrost (jedną wartość) - to przez te "and" nie łapie poprawnie wyników. Jak to powinienem poprawnie zapisać? :)

0

Dziękuję bardzo :) Jest mały problem. Jeśli użytkownik świadczy usługi A,B i C z puli usług A,B,C,D,E - zaznaczę że szukam usługi A,B,C,D,E - to użytkownik pomimo tego iż nie świadczy wszystkich szukanych usług - i tak zostanie wyświetlony na liście wyników (a nie powinien - nie świadczy wszystkich szukanych usług)

W SQL konstrukcja typu

SELECT * FROM table_name WHERE id IN (1,2,3);

jest tłumaczona do

SELECT * FROM table_name WHERE id='1' OR id='2' OR id='3';

Jeżeli chcesz, aby zapytanie wymagało posiadania wszystkich kryteriów, to musisz odpowiednio stworzyć zapytanie (czyli musi być AND, nie OR).

A jako iż użytkownik ma jeden wzrost (jedną wartość) - to przez te "and" nie łapie poprawnie wyników. Jak to powinienem poprawnie zapisać? :)

Zamiast AND użyj OR.

0

Dziękuję bardzo za odpowiedź.
Usługi działają poprawnie, jednak kwestia wzrostu jednak pozostaje problematyczna :(

Jeśli dobrze zrozumiałem Twoją sugestię, to powinienem iść w kierunków "or". Tak też zrobiłem:

if (!is_null(request('s_height'))) {
            $selectedId = [];
            foreach (request('s_height') as $i => $id) {
                if ($id != 0) {
                    array_push($selectedId, $id);
                }
            }
            $query->whereIn('user_height', $selectedId);
        }

w wyniku takiego zapytania mam wynik: .... and user_height in ('1', '2', '3', '4', '5').
Powinny być raczej przedziały: [0, 150], [151, 160], [161, 170], [171, 180], [181, 500]...
Wiem że mogę sobie pozamieniać 1=> [0,150] etc jednak dałoby to w powyższym układzie zapytanie w stylu:

SELECT * FROM `users` WHERE  `user_height ` in ([161, 170],[171, 180])

co byłoby błędne....

Wydaje mi się że trzeba by zmienić na coś tego:

$query->whereIn('user_height', $selectedId);

?

0

Jak masz przedział wzrostu 161 - 170, to użyj between w where.

0
serek napisał(a):

Jak masz przedział wzrostu 161 - 170, to użyj between w where.

$query->whereBetween('user_height', [161, 170]);

takie coś próbowałem. Użytkownik ma 1 przypisany wzrost. Jeśli w wyszukiwarce zaznaczę wszystkie przedziały - to nie zwraca mi wyniku. Działało to dobrze przy wyborze 1 - właściwego przedziału....

0
lukmopy napisał(a):
serek napisał(a):

Jak masz przedział wzrostu 161 - 170, to użyj between w where.

$query->whereBetween('user_height', [161, 170]);

takie coś próbowałem. Użytkownik ma 1 przypisany wzrost. Jeśli w wyszukiwarce zaznaczę wszystkie przedziały - to nie zwraca mi wyniku. Działało to dobrze przy wyborze 1 - właściwego przedziału....

Musisz tyle razy ile masz przedziałów, to tyle samo dodać tych between.

0
serek napisał(a):
lukmopy napisał(a):
serek napisał(a):

Jak masz przedział wzrostu 161 - 170, to użyj between w where.

$query->whereBetween('user_height', [161, 170]);

takie coś próbowałem. Użytkownik ma 1 przypisany wzrost. Jeśli w wyszukiwarce zaznaczę wszystkie przedziały - to nie zwraca mi wyniku. Działało to dobrze przy wyborze 1 - właściwego przedziału....

Musisz tyle razy ile masz przedziałów, to tyle samo dodać tych between.

takie coś próbowałem:


foreach (request('s_height') as $i => $id) {
                if ($id != 0) {
                    if ($id == 1) {
                        $query->whereBetween('user_height', [0, 150]);
                    } elseif ($id == 2) {
                        $query->whereBetween('user_height', [151, 160]);
                    } elseif ($id == 3) {
                        $query->whereBetween('user_height', [161, 170]);
                    } elseif ($id == 4) {
                        $query->whereBetween('user_height', [171, 180]);
                    } elseif ($id == 5) {
                        $query->whereBetween('user_height', [181, 500]);
                    }
                }
            }

daje taki sql:


and `user_height ` between 0 and 150 and `user_height ` between 151 and 160 and `user_height ` between 161 and 170 and `user_height ` between 171 and 180 and `user_height ` between 181 and 500

i nie znajduje wyników w momencie, gdy jest zaznaczony więcej niż 1 wzrost w wyszukiwarce :(

0

Kurde no, podstawy logiki się kłaniają. Przeczytaj sobie jeszcze raz ten wynikowy sql, powoli. I zobaczysz co jest nie tak.

0
serek napisał(a):

Kurde no, podstawy logiki się kłaniają. Przeczytaj sobie jeszcze raz ten wynikowy sql, powoli. I zobaczysz co jest nie tak.

Moim zdaniem powinno wyglądać to tak:

and (`user_height ` between 0 and 150) or  (`user_height ` between 151 and 160) or( `user_height ` between 161 and 170) or (`user_height ` between 171 and 180) or (`user_height ` between 181 and 500)

tylko nie wiem jak ubrać to w te:


 $query->whereBetween('user_height', [161, 170]);

0

->orWhereBetween('nazwa', [x, y])

?

0
serek napisał(a):

->orWhereBetween('nazwa', [x, y])

?

w tej chwili zwraca coś takiego:

and (` `enable` = 1 and `email_verified_at` is not null) or `user_height` between 0 and 150 or `user_height ` between 151 and 160 or `user_height ` between 161 and 170 or `user_height ` between 171 and 180 or `user_height ` between 181 and 500

i teraz zwraca mi wszystkie rekordy, nawet te ze statusem enable != 1. Trzeba jakoś dodać te nawiasy:


and (`user_height` between 0 and 150) or (`user_height ` between 151 and 160) or (`user_height ` between 161 and 170) or (`user_height ` between 171 and 180) or ( `user_height ` between 181 and 500)


0
$q->where(function($query){
    $query
    ->whereBetween(...)
    ->orWhereBetween(...)
    ->orWhereBetween(...);
});
0
mefsh napisał(a):
$q->where(function($query){
    $query
    ->whereBetween(...)
    ->orWhereBetween(...)
    ->orWhereBetween(...);
});

Aktualnie ten kod zwraca:

... and ( `enable` = 1 and `email_verified_at` is not null) and `user_height` between 0 and 150 or `user_height ` between 151 and 160 or `user_height ` between 161 and 170 or `user_height ` between 171 and 180 or `user_height ` between 181 and 500

W tej chwili wybieram w wyszukiwarce pierwszy przedział (0-150). Jest w dalszym ciągu błąd. Np. użytkownik który ma 120 cm jest widziany w każdym przedziale

0

Człowieku pomyśl sam trochę, a nie wklejaj bezmyślnie kod, który ktoś Ci poda.

Skoro w zapytaniu bierzesz pod uwagę wszystkie przedziały, zamiast jednego, to chyba nic dziwnego, że zwraca zły wynik?

0

W tej chwili wybieram w wyszukiwarce pierwszy przedział (0-150). Jest w dalszym ciągu błąd. Np. użytkownik który ma 120 cm jest widziany w każdym przedziale

120 mieści się w 0-150.
Upewnij się że dołączone do zapytania są tylko przedziały zaznaczone/wybrane przez Ciebie, bo po zapytaniu widzę, że dodane są tam wszystkie możliwe przedziały - nie dziwne więc, że zawsze Ci zwraca ten rekord.

1 użytkowników online, w tym zalogowanych: 0, gości: 1