2016-12-05 16 views
3

我想爲食譜及其相關成分實現搜索功能。用戶應該指定他想要從搜索中排除的成分,同時指定他正在尋找的配方中包含的成分。Cakephp3.1:一次在同一個關聯模型上使用匹配()和notMatching()

那些是我的兩個發現者:

public function findByContainingIngredients(Query $query, array $params) 
{ 
    $ingredients = preg_replace('/\s+/', '', $params['containing_ingredients']); 

    if($ingredients) { 
     $ingredients = explode(',', $ingredients); 
     $query->distinct(['Recipes.id']); 
     $query->matching('Ingredients', function ($query) use($ingredients) { 
      return $query->where(function ($exp, $query) use($ingredients) { 
       return $exp->in('Ingredients.title', $ingredients); 
      }); 
     }); 
    } 
    return $query; 
} 


public function findByExcludingIngredients(Query $query, array $params) 
{ 
    $ingredients = preg_replace('/\s+/', '', $params['excluding_ingredients']); 

    if($ingredients) { 
     $ingredients = explode(',', $ingredients); 
     $query->distinct(['Recipes.id']); 
     $query->notMatching('Ingredients', function ($query) use ($ingredients) { 
      return $query->where(function ($exp, $query) use ($ingredients) { 
       return $exp->in('Ingredients.title', $ingredients); 
      }); 
     }); 

    } 
    return $query; 
} 

在控制器I撥打:

  $recipes = $this->Recipes->find() 
      ->find('byExcludingIngredients', $this->request->data) 
      ->find('byContainingIngredients', $this->request->data); 

如果用戶從搜索中排除的成分,並指定他要包括一個或多個成分,結果爲零。 當我看看生成的SQL我看到的問題:

SELECT 
    Recipes.id AS `Recipes__id`, 
    Recipes.title AS `Recipes__title`, 
    ..... 

FROM 
    recipes Recipes 
    INNER JOIN ingredients Ingredients ON (
    Ingredients.title IN (: c0) 
    AND Ingredients.title IN (: c1) 
    AND Recipes.id = (Ingredients.recipe_id) 
) 
WHERE 
    (
    Recipes.title like '%%' 
    AND (Ingredients.id) IS NULL 
) 
GROUP BY 
    Recipes.id, 
    Recipes.id 

的問題是 「AND(Ingredients.id)IS NULL」。這條線使得包含成分的結果消失。 我的方法:

  • 在關聯兩次調用notMatching()時創建一個別名。我認爲這在Cake3.1中是不可能的
  • 在PK/FK和排除的標題上使用左連接並創建別名。基本上寫我自己的notMatching函數。這有效,但它感覺不對。

還有其他解決方案嗎?

回答

1

任何人來到這個頁面和結論,你不能結合一個matching()notMatching()使用在同一個關聯的類中:

是的,它有可能(無論如何,從Cake 3.4.9開始)做這樣的發現。但是您必須爲目標表使用不同的別名 - 這是與通常的類名稱不同的別名。

所以在OP的情況下,你會把這RecipesTable.php

public function initialize(array $config) { 
    ... usual stuff 

    $this->belongsToMany('Ingredients', [ 
     'foreignKey' => 'recipe_id', 
     'targetForeignKey' => 'ingredient_id', 
     'joinTable' => 'ingredients_recipes' 
    ]); 
    // the next association uses an alias, 
    // but is otherwise *exactly* the same as the previous assoc. 
    $this->belongsToMany('ExcludedIngredients', [ 
     'className' => 'Ingredients', 
     'foreignKey' => 'recipe_id', 
     'targetForeignKey' => 'ingredient_id', 
     'joinTable' => 'ingredients_recipes' 
    ]); 
} 

你應該能夠編寫一個查找聲明是這樣的:

$this->find() 
    -> ... usual stuff 
    ->matching('Ingredients',function($q) use($okIngredients) { 
     ... check for ingredients ... 
    }) 
    ->notMatching('ExcludedIngredients', function($q) use($excludedIngredients) { 
     ... check for ingredients ... 
    }); 

確實工作。不幸的是,當我在與'Recipes'表中的千行類似的情況下使用它時,查詢耗時40秒來運行。所以我不得不回去用手工連接來代替notMatching()

相關問題