Filtrowanie wyników w Laravel equivalent relationship

0

Witam serdecznie.
Mam mały problem z Laravelem.

Mam taki kod:


Schema::create('dish_values', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->bigInteger('dishes_id')->unsigned();
            $table->foreign('dishes_id')->references('id')->on('dishes')->onDelete('cascade');
            $table->bigInteger('food_ingredient_id')->unsigned();
            $table->foreign('food_ingredient_id')->references('id')->on('food_ingredients');
            $table->bigInteger('company_id')->unsigned();
            $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade');
            $table->decimal('quantity', 9, 2)->default(0);
            $table->engine = "InnoDB";
            $table->charset = 'utf8mb4';
            $table->collation = 'utf8mb4_unicode_ci';
        });


        Schema::create('dishes', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->bigInteger('company_id')->unsigned();
            $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade');
            $table->string('name', 100)->nullable();
            $table->longText('description')->nullable();
            $table->char('enable', 1)->default(1);
            $table->char('allergen', 1)->default(0);
            $table->engine = "InnoDB";
            $table->charset = 'utf8mb4';
            $table->collation = 'utf8mb4_unicode_ci';
        });

        Schema::create('food_ingredients', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->bigInteger('company_id')->unsigned();
            $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade');
            $table->string('name', 120)->nullable();
            $table->decimal('garbage', 9, 2)->default(0);
            $table->decimal('energy_value', 9, 2)->default(0);
            $table->decimal('protein', 9, 2)->default(0);
            $table->decimal('fat', 9, 2)->default(0);
            $table->decimal('available_carbohydrates', 9, 2)->default(0);
            $table->decimal('roughage', 9, 2)->default(0);
            $table->longText('description')->nullable();
            $table->string('url_address', 160);
            $table->char('allergen', 1)->default(0);
            $table->char('allergen1', 1)->default(0);
            $table->char('allergen2', 1)->default(0);
            $table->char('allergen3', 1)->default(0);
            $table->char('available_in_demo', 1)->default(0);
            $table->char('enable', 1)->default(1);
            $table->engine = "InnoDB";
            $table->charset = 'utf8mb4';
            $table->collation = 'utf8mb4_unicode_ci';
        });



class Dish extends Model
{
    protected $quarded = ['id'];
    protected $fillable = ['company_id', 'name', 'description',  'enable', 'allergen'];
    public $timestamps = false;

    public function components()
    {
        return $this->hasManyThrough('App\DishValues', 'App\Dish', 'id', 'dishes_id');
    }

    public function foodIngredient()
    {
        return $this->hasManyThrough('App\FoodIngredient', 'App\DishValues', 'dishes_id', 'id');
    }

    public function withoutAllergen()
    {
        return $this->foodIngredient()->where('allergen1', '=', '1');
    }
}

Mam funkcję do wyświetlenia danych:


Dish::where('enable', '=', 1)->with('components')
                ->where(function($q)use ($companyId) {
                    $q->where('company_id', $companyId) 
                        ->orWhere('company_id', 1);
                })
                ->where([
                ['name', 'LIKE', '%' . $query . '%'],
                ['enable', '=', 1],
            ])->get()

Powyższy kod działa poprawnie.
Chciałbym filtrować po alergenach:


Dish::where('enable', '=', 1)->with('components')
                ->where(function($q)use ($companyId) {
                    $q->where('company_id', $companyId)->where('allergen1', 1)
                        ->orWhere('company_id', 1);
                })
                ->where([
                ['name', 'LIKE', '%' . $query . '%'],
                ['enable', '=', 1],
            ])->get()

Jednak otrzymuje taki błąd:

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'allergen1' in 'where clause' (SQL: select count(*) as aggregate from dishes where enable = 1 and allergen1 = 1 and (name LIKE %% and enable = 1 and enable = 1))

Wie ktoś może jak to naprawić?

0

Tabela dishes nie zawiera kolumny allergen1, więc nie możesz po niej wyszukiwać.

0

food_ingredients - tutaj mam allergen1 :) i po niej chciałbym wyszukiwać

taka kombinacja też nie działa:


Dish::where('enable', '=', 1)->with('foodIngredient')
                ->where(function($q)use ($companyId) {
                    $q->where('company_id', $companyId)->where('allergen1', 1)
                        ->orWhere('company_id', 1);
                })
                ->where([
                ['name', 'LIKE', '%' . $query . '%'],
                ['enable', '=', 1],
            ])->get()
0

Spróbuj foodIngredient.allergen1, a najlepiej pokaż wygenerowany kod SQL dla tego ostatniego podejścia.

0
Patryk27 napisał(a):

Spróbuj foodIngredient.allergen1, a najlepiej pokaż wygenerowany kod SQL dla tego ostatniego podejścia.

STATE[42S22]: Column not found: 1054 Unknown column 'allergen1' in 'where clause' (SQL: select * from `dishes` where `enable` = 1 and (`company_id` = 1 and `allergen1` = 1 or `company_id` = 1) and (`name` LIKE %d% and `enable` = 1))
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'allergen1' in 'where clause' (SQL: select * from `dishes` where `enable` = 1 and (`company_id` = 1 and `allergen1` = 1 or `company_id` = 1) and (`name` LIKE %d% and `enable` = 1))

Wariant po Twojej zmianie:

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'foodIngredient.allergen1' in 'where clause' (SQL: select * from `dishes` where `enable` = 1 and (`company_id` = 1 and `foodIngredient`.`allergen1` = 1 or `company_id` = 1) and (`name` LIKE %d% and `enable` = 1))
0

Ach, no tak - with() nie modyfikuje zapytania :-P
Zrób tam joina.

0
Patryk27 napisał(a):

Ach, no tak - with() nie modyfikuje zapytania :-P
Zrób tam joina.

Mam teraz kod:


Dish::where('enable', '=', 1)->join('foodIngredient')
                ->where(function($q)use ($companyId) {
                    $q->where('company_id', $companyId)->where('foodIngredient.allergen1', 1)
                        ->orWhere('company_id', 1);
                })
                ->where([
                    ['name', 'LIKE', '%' . $query . '%'],
                    ['enable', '=', 1],
                ])->get();


Tak ma być?:)

Zwraca mi błąd:

Too few arguments to function Illuminate\Database\Query\Builder::join(), 1 passed in /usr/home/webplay/domains/symfonia.newapp.ovh/vendor/laravel/framework/src/Illuminate/Support/Traits/ForwardsCalls.php on line 23 and at least 2 expecte

0

Mam taki kod:


Dish::join('dish_values', 'dish_values.dishes_id', '=', 'dishes.id')->join('food_ingredients', 'dish_values.food_ingredient_id', '=', 'food_ingredients.id')
                ->where(function($q)use ($companyId) {
                    $q->where('dishes.company_id', $companyId)
                        ->orWhere('dishes.company_id', 1);
                })
                ->where([
                    ['dishes.name', 'LIKE', '%' . $query . '%'],
                    ['dishes.enable', '=', 1],
                    ['food_ingredients.allergen', '=', 0],
                ])->paginate(8);

Generuje SQL:


select * from `dishes` inner join `dish_values` on `dish_values`.`dishes_id` = `dishes`.`id` inner join `food_ingredients` on `dish_values`.`food_ingredient_id` = `food_ingredients`.`id` where (`dishes`.`company_id` = 1 or `dishes`.`company_id` = 1) and (`dishes`.`name` LIKE '%g%' and `dishes`.`enable` = 1 and `food_ingredients`.`allergen` = 0) limit 8 offset 0

W wyniku zwraca mi nazwę składników z pominięciem tego, który ma alergen, a nie potraw jak wcześniej :(
W wyniku zapytania powinny wrócić potrawy, które nie mają w składnikach alergenów.

Co zrobiłem źle?

0

Wcześniej porównywałeś allergen1 = 1, teraz robisz allegen = 0 - co to w ogóle znaczy? Co te kolumny reprezentują?

0

Inny alergen. Bez różnicy czy allergen czy allergen1, bo dla obu zasada jest identyczna. Jeśli danie ma allergen lub allergen = 2 - to jest daniem z alergenami

0

Wydaje mi się, że nie rozumiem nadal schematu bazy :-P

Mógłbyś wrzucić jakiś screenshot z przykładowymi danymi oraz tym, co chciałbyś, aby zapytanie zwracało?

0

Ok, już tłumaczę jeszcze raz :)

Mam tabele:


CREATE TABLE `dishes` (
  `id` bigint(20) UNSIGNED NOT NULL,
  `company_id` bigint(20) UNSIGNED NOT NULL,
  `name` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `description` longtext COLLATE utf8mb4_unicode_ci,
  `enable` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '1',
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;


CREATE TABLE `dish_values` (
  `id` bigint(20) UNSIGNED NOT NULL,
  `dishes_id` bigint(20) UNSIGNED NOT NULL,
  `food_ingredient_id` bigint(20) UNSIGNED NOT NULL,
  `company_id` bigint(20) UNSIGNED NOT NULL,
  `quantity` decimal(9,2) NOT NULL DEFAULT '0.00'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;


CREATE TABLE `food_ingredients` (
  `id` bigint(20) UNSIGNED NOT NULL,
  `company_id` bigint(20) UNSIGNED NOT NULL,
  `name` varchar(120) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `garbage` decimal(9,2) NOT NULL DEFAULT '0.00',
  `energy_value` decimal(9,2) NOT NULL DEFAULT '0.00',
  `protein` decimal(9,2) NOT NULL DEFAULT '0.00',
  `fat` decimal(9,2) NOT NULL DEFAULT '0.00',
  `available_carbohydrates` decimal(9,2) NOT NULL DEFAULT '0.00',
  `roughage` decimal(9,2) NOT NULL DEFAULT '0.00',
  `description` longtext COLLATE utf8mb4_unicode_ci,
  `url_address` varchar(160) COLLATE utf8mb4_unicode_ci NOT NULL,
  `allergen` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0',
  `allergen1` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0',
  `allergen2` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0',
  `allergen3` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0',
  `available_in_demo` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0',
  `enable` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '1'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

Moje tabele oznaczają
tabela dishes = nazwy dań
tabela dish_values = składniki z jakich dania są zbudowane
tabela food_ingredients = baza składników.

Potrzebuję wyświetlić dania (dishes), które mają w sobie alergeny (lub ich nie mają).
Czyli w składnikach (food_ingredients) pola: allergen, allergen1, allergen2, allergen3 - muszą być 0 - gdy składnik nie jest alergenem) lub 1 - gdy składnik jest alergenem.

Obrazowo bardziej:
w tabeli dishes - tutaj mamy potrawy: zupa z kurczaka, pieczeń z dzika, sałatka z kolendrą.
w tabeli dish_values - mamy składniki i gramatury z czego zbudowane są powyższe dania
food_ingredients - to składniki - sól, cukier, mleko, mąka itp. Tutaj mam oznaczone które z tych składników są alergenami.

Muszę wyświetlić wszystkie dania które nie mają / lub mają w składzie alergeny.

Mam nadzieję że teraz bardziej obrazowo to napisałem :)

0

Oki, teraz to ma sens :-)

Spróbujw takim razie whereHas() zamiast join(), powinno zadziałać prawidłowo.

0

Chyba coś jeszcze jest nie tak :(

Napisałem taki kod:

Dish::whereHas('foodIngredient', function($q) use($query, $companyId)
            {
                $q->whereHas('food_ingredients', function($q) use($query, $companyId){
                        $q->where([['allergen1', 1], ['dishes.company_id', 1]])
                            ->orWhere([['allergen1', 1], ['dishes.company_id', $companyId]]);
                    });
            })->where([
                ['dishes.name', 'LIKE', '%' . $query . '%'],
                ['dishes.enable', '=', 1],
            ])->paginate(8)

Ale otrzymuję błąd:
Call to undefined method App\FoodIngredient::food_ingredients()

0

Ok, zrobiłem taki kod:


$test = Dish::whereHas('foodIngredient', function ($q) use ($query, $companyId, $allergen) {
                if($allergen == 0){ // allergen
                    $q->where([['food_ingredients.allergen', 0], ['dishes.company_id', 1]])->orWhere([['food_ingredients.allergen', 0], ['dishes.company_id', $companyId]]);
                }
                if($allergen == 1){ // allergen 1
                    $q->where([['allergen1', 0], ['dishes.company_id', 1]])->orWhere([['allergen1', 0], ['dishes.company_id', $companyId]]);
                }
                if($allergen == 2){ // allergen 2
                    $q->where([['allergen2', 0], ['dishes.company_id', 1]])->orWhere([['allergen2', 0], ['dishes.company_id', $companyId]]);
                }
                if($allergen == 3){ // allergen 3
                    $q->where([['allergen3', 0], ['dishes.company_id', 1]])->orWhere([['allergen3', 0], ['dishes.company_id', $companyId]]);
                }
            })->where([
                ['dishes.name', 'LIKE', '%' . $query . '%'],
                ['dishes.enable', '=', 1],
            ])->paginate(8);
            dd($test);

i działa, jednak nie zwraca uwagi na te alergeny - za każdym razem pokazuje wszystkie rekordy :/

Zapytanie SQL:

select * from `dishes` where exists (select * from `food_ingredients` inner join `dish_values` on `dish_values`.`id` = `food_ingredients`.`id` where `dishes`.`id` = `dish_values`.`dishes_id` and ((`food_ingredients`.`allergen` = 0 and `dishes`.`company_id` = 1) or (`food_ingredients`.`allergen` = 0 and `dishes`.`company_id` = 1))) and (`dishes`.`name` LIKE '%o%' and `dishes`.`enable` = 1)

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