addFilter vs addFieldToFilter


19

Magento collection มีวิธีการกรองสองวิธี:

1. Varien_Data_Collection_Db::addFieldToFilter
2. Varien_Data_Collection::addFilter

Zend_Db_Selectดูเหมือนว่าทั้งสองวิธีเพิ่มที่เงื่อนไข และข้อดีอะไรaddFilterนำมา? เมื่อฉันควรใช้มันแทนaddFieldToFilter?

คำตอบ:


49

ตกลงเรามาตรวจสอบพวกเขา ความแตกต่างแรกคือว่าaddFilter()ทั่วไปมากกว่าและไม่เฉพาะฐานข้อมูล นอกจากนี้ยังใช้Varien_Directory_Collectionเพื่อกรองตามชื่อไฟล์ Varien_Data_Collection_Dbแต่สำหรับคำตอบนี้ฉันกำลังจะมุ่งเน้น

วิธีการเหล่านี้มีลายเซ็นที่แตกต่างกันซึ่งaddFilterดูเหมือนว่าจะมีความยืดหยุ่นน้อยลง แต่คุณจะเห็นว่ามันมีข้อดีเช่นกัน:

1. addFieldToFilter ()

/**
 * Add field filter to collection
 *
 * @see self::_getConditionSql for $condition
 *
 * @param   string|array $field
 * @param   null|string|array $condition
 *
 * @return  Mage_Eav_Model_Entity_Collection_Abstract
 */
public function addFieldToFilter($field, $condition = null)

พารามิเตอร์

addFieldToFilter () สามารถใช้อาร์เรย์ของฟิลด์ด้วยอาร์เรย์ของเงื่อนไขหรือฟิลด์เดียวที่มีเงื่อนไขเดียว:

  • addFieldToFilter('field', 'value')

    ผลลัพธ์ใน: field=value

  • addFieldToFilter(['field1', 'field2'], ['value1', 'value2']);

    ผลลัพธ์ใน: field1=value1 OR field2=value2

แต่ละเงื่อนไขสามารถ:

  • ค่าสเกลาร์เดี่ยว (เหมือน'value1'และ'value2'สูงกว่า)
  • อาร์เรย์ในรูปแบบ [ operator => value ]
  • Zend_Db_Exprวัตถุ
  • อาร์เรย์ของเงื่อนไขที่รวมกับ "หรือ" (ใช่นั่นคือเรียกซ้ำ)

โดยเฉพาะอย่างยิ่งไวยากรณ์ "โอเปอเรเตอร์ => ค่า" ได้รับการบันทึกไว้ในรหัสที่Varien_Db_Adapter_Pdo_Mysql::prepareSqlCondition()- จำไว้ว่าฉันค้นหามันบ่อยๆ:

 * If $condition integer or string - exact value will be filtered ('eq' condition)
 *
 * If $condition is array - one of the following structures is expected:
 * - array("from" => $fromValue, "to" => $toValue)
 * - array("eq" => $equalValue)
 * - array("neq" => $notEqualValue)
 * - array("like" => $likeValue)
 * - array("in" => array($inValues))
 * - array("nin" => array($notInValues))
 * - array("notnull" => $valueIsNotNull)
 * - array("null" => $valueIsNull)
 * - array("moreq" => $moreOrEqualValue)
 * - array("gt" => $greaterValue)
 * - array("lt" => $lessValue)
 * - array("gteq" => $greaterOrEqualValue)
 * - array("lteq" => $lessOrEqualValue)
 * - array("finset" => $valueInSet)
 * - array("regexp" => $regularExpression)
 * - array("seq" => $stringValue)
 * - array("sneq" => $stringValue)
 *
 * If non matched - sequential array is expected and OR conditions
 * will be built using above mentioned structure

มีคุณสมบัติที่ไม่มีเอกสารเพิ่มเติมในfrom/ toโอเปอเรเตอร์:

  • กับและค่าจะถูกแยกเป็นวันที่ พวกเขาสามารถอยู่ในรูปแบบใด ๆ ที่เป็นที่ยอมรับโดย['from' => $dateFrom, 'to' => $dateTo, 'date' => true]$dateFrom$dateToVarien_Date::formatDate()
  • ถ้าคุณต้องการวันที่แยกคุณลักษณะ แต่เพียงเพื่อเปรียบเทียบหนึ่ง<=หรือ>=คุณสามารถละเว้นอย่างใดอย่างหนึ่งหรือ'from''to'
  • 'datetime' => trueควรจะทำงานมากเกินไปและรวมถึงเวลาที่ไม่เพียง แต่วัน แต่มีข้อผิดพลาดในVarien_Db_Adapter_Pdo_Mysql :: _ prepareSqlDateCondition () (หายไป$includeTimestampพารามิเตอร์) ซึ่งจะทำให้การทำงานในลักษณะเดียวกันเป็นdatetime ทั้งสองรวมเวลา ดังนั้นหากคุณต้องการที่จะเปรียบเทียบโดยปัจจุบันมีเพียงเพิ่มไปยังวันที่และกับวันที่date00:00:00from23:59:59to

การทำแผนที่ภาคสนาม

วิธีใช้การแมปฟิลด์ การแม็ปฟิลด์สามารถกำหนดในคลาสคอลเลกชันคอนกรีตเพื่อสร้างชื่อฟิลด์นามแฝง นี่คือตัวอย่างจากการรวบรวมผลิตภัณฑ์:

protected $_map = array('fields' => array(
    'price'         => 'price_index.price',
    'final_price'   => 'price_index.final_price',
    'min_price'     => 'price_index.min_price',
    'max_price'     => 'price_index.max_price',
    'tier_price'    => 'price_index.tier_price',
    'special_price' => 'price_index.special_price',
));

2. addFilter ()

/**
 * Add collection filter
 *s
 * @param string $field
 * @param string $value
 * @param string $type and|or|string
 */
public function addFilter($field, $value, $type = 'and')

พารามิเตอร์

addFilter()อนุญาตให้กรองฟิลด์เดียวด้วยค่าเดียวและประเภทเท่านั้น $typeสามารถใด ๆ ของ:

  • "และ" (ค่าเริ่มต้น) - เพิ่มAND $field=$valueไว้ในส่วนคำสั่ง WHERE (แน่นอนว่ามีการอ้างอิงที่เหมาะสม)
  • "หรือ" - เพิ่ม"OR $field=$valueไว้ในส่วนคำสั่ง WHERE (เหมือนกัน)
  • "string" - เพิ่มAND $valueไว้ใน WHERE clause (เช่น $ value สามารถเป็นนิพจน์ SQL โดยพลการ)
  • "ประชาชน" - ใช้ทำแผนที่สนามและเพื่อที่คล้ายกัน_getConditionSql() addFieldToFilter()สิ่งนี้ทำให้มีประสิทธิภาพเกือบจะเป็นเพียงขาดคุณสมบัติในการเพิ่มตัวกรองหลายตัวสำหรับเขตข้อมูลต่าง ๆ รวมกับ OR

ในVarien_Data_Collection_Db::_renderFilters()คุณสามารถดูว่าพวกเขามีการประมวลผลอย่างไร

ขยาย

addFilter()มีสิ่งหนึ่งที่แตกต่างที่สำคัญซึ่งเป็นข้อได้เปรียบคือ มันรวบรวมตัวกรองที่จะนำไปใช้$this->_filters()และเพิ่มไปยังZend_Db_Selectวัตถุแบบสอบถามได้ทันทีก่อนที่จะโหลดคอลเลกชัน addFieldToFilter()ในทางกลับกันจัดการวัตถุแบบสอบถามทันที

สิ่งนี้ช่วยให้คุณสามารถจัดการหรือลบตัวกรองที่เพิ่มไปแล้ว คอลเลกชัน Varien ไม่มีส่วนต่อประสานสำหรับอินเทอร์เฟซคุณต้องใช้สิ่งนี้ในคอลเลกชันที่คุณกำหนดเอง มีวิธีการขอ_renderFiltersBefore()ที่คุณสามารถแทนที่


ฉันมีคำถามหนึ่งข้อที่เราสามารถใช้ได้addFilterกับattributes?
Murtuza Zabuawala

@MurtuzaZabuawala ไม่ไม่สามารถใช้กับแอตทริบิวต์ EAV ได้
Fabian Schmengler

ขอบคุณสำหรับคำตอบนี้ Fabian ฉันสนุกกับการโพสต์เว็บไซต์ของคุณเกี่ยวกับเรื่องนี้เช่นกัน แต่ฟิลด์ $ สามารถมีค่าอะไรบ้างใน addFilter ฉันกำลังพยายามใช้ฟังก์ชั่น addFilter เพื่อกรองเฉพาะผลิตภัณฑ์ที่อยู่ในหมวดหมู่ที่โมดูลกำลังทำงานอยู่
John

AFAIK ไม่สามารถทำได้เนื่องจากหมวดหมู่ไม่ใช่แอตทริบิวต์ แต่เชื่อมโยงกับผลิตภัณฑ์ในตารางแยกต่างหาก ไม่สามารถแก้ปัญหาให้คุณได้บนหัวของฉันขอโทษ
เฟเบียน Schmengler

ขอบคุณสำหรับการตอบสนองไม่ต้องกังวลหากฉันพบวิธีแก้ไขฉันจะอัปเดตที่นี่พร้อมทางออกของฉัน
John

2

Magento collection มีวิธีการกรองสองแบบที่แตกต่างกัน

  1. Varien_Data_Collection_Db :: addFieldToFilter

addFieldToFilter (เขตข้อมูล $, $ เงื่อนไข = null)

พารามิเตอร์แรกของaddFieldToFilterคือแอตทริบิวต์ที่คุณต้องการกรอง อย่างที่สองคือค่าที่คุณกำลังมองหา นี่เรากำลังเพิ่มตัวกรองสำหรับค่าskun2610

พารามิเตอร์ที่สองยังสามารถใช้เพื่อระบุประเภทของการกรองที่คุณต้องการ นี่คือสิ่งที่ซับซ้อนเล็กน้อยและคุ้มค่าที่จะลงลึกมากขึ้น

ดังนั้นโดยค่าเริ่มต้นต่อไปนี้

$collection_of_products->addFieldToFilter('sku','n2610'); 

เท่ากับ (เป็นหลัก) เทียบเท่ากับ

WHERE sku = "n2610"

ลองดูด้วยตัวคุณเอง วิ่งต่อไปนี้

public function testAction()
{
    var_dump(
    (string) 
    Mage::getModel('catalog/product')
    ->getCollection()
    ->addFieldToFilter('sku','n2610')
    ->getSelect());
}

จะให้ผลผลิต

SELECT `e`.* FROM `catalog_product_entity` AS `e` WHERE (e.sku = 'n2610')'

โปรดทราบว่าสิ่งนี้อาจซับซ้อนได้อย่างรวดเร็วหากคุณใช้แอตทริบิวต์ EAV เพิ่มคุณสมบัติ

var_dump(
(string) 
Mage::getModel('catalog/product')
->getCollection()
->addAttributeToSelect('*')
->addFieldToFilter('meta_title','my title')
->getSelect()
);

และแบบสอบถามได้รับ gnarly

SELECT `e`.*, IF(_table_meta_title.value_id>0, _table_meta_title.value, _table_meta_title_default.value) AS `meta_title` 
FROM `catalog_product_entity` AS `e` 
INNER JOIN `catalog_product_entity_varchar` AS `_table_meta_title_default` 
    ON (_table_meta_title_default.entity_id = e.entity_id) AND (_table_meta_title_default.attribute_id='103') 
    AND _table_meta_title_default.store_id=0        
LEFT JOIN `catalog_product_entity_varchar` AS `_table_meta_title` 
    ON (_table_meta_title.entity_id = e.entity_id) AND (_table_meta_title.attribute_id='103') 
    AND (_table_meta_title.store_id='1') 
WHERE (IF(_table_meta_title.value_id>0, _table_meta_title.value, _table_meta_title_default.value) = 'my title')

อย่าเชื่อในประเด็นนี้ แต่อย่าคิดมากเกี่ยวกับ SQL มากเกินไปถ้าคุณกำลังจะถึงกำหนด

ตัวดำเนินการเปรียบเทียบอื่นฉันแน่ใจว่าคุณสงสัยว่า“ จะเป็นเช่นไรหากฉันต้องการบางอย่างที่ไม่ใช่ข้อความค้นหา” ไม่เท่ากันมากกว่าน้อยกว่า ฯลฯ พารามิเตอร์ที่สองของเมธอด addFieldToFilter ทำให้คุณครอบคลุมได้เช่นกัน สนับสนุนไวยากรณ์ทางเลือกโดยที่แทนที่จะส่งผ่านสตริงคุณจะผ่านในอาเรย์องค์ประกอบเดียว

กุญแจสำคัญของอาร์เรย์นี้คือประเภทของการเปรียบเทียบที่คุณต้องการ ค่าที่เกี่ยวข้องกับคีย์นั้นคือค่าที่คุณต้องการกรอง ลองทำซ้ำตัวกรองด้านบน แต่ด้วยไวยากรณ์ที่ชัดเจนนี้

public function testAction()
{
    var_dump(
    (string) 
    Mage::getModel('catalog/product')
    ->getCollection()
    ->addFieldToFilter('sku',array('eq'=>'n2610'))
    ->getSelect()
    );          
}

โทรออกตัวกรองของเรา

addFieldToFilter('sku',array('eq'=>'n2610'))

อย่างที่คุณเห็นพารามิเตอร์ตัวที่สองคือ PHP Array มันคือกุญแจสำคัญ eq ซึ่งหมายถึงเท่ากับ ค่าสำหรับคีย์นี้คือ n2610 ซึ่งเป็นค่าที่เรากำลังกรอง

Magento มีภาษาอังกฤษจำนวนหนึ่งเช่นตัวกรองที่จะทำให้ความทรงจำฉีกขาด (และอาจจะเจ็บปวด) กับนักพัฒนา perl เก่า ๆ ในกลุ่มผู้ชม

รายการด้านล่างเป็นตัวกรองทั้งหมดพร้อมกับตัวอย่างของการเทียบเท่า SQL

array("eq"=>'n2610')
WHERE (e.sku = 'n2610')

array("neq"=>'n2610')
WHERE (e.sku != 'n2610')

array("like"=>'n2610')
WHERE (e.sku like 'n2610')

array("nlike"=>'n2610')
WHERE (e.sku not like 'n2610')

array("is"=>'n2610')
WHERE (e.sku is 'n2610')

array("in"=>array('n2610'))
WHERE (e.sku in ('n2610'))

array("nin"=>array('n2610'))
WHERE (e.sku not in ('n2610'))

array("notnull"=>'n2610')
WHERE (e.sku is NOT NULL)

array("null"=>'n2610')
WHERE (e.sku is NULL)

array("gt"=>'n2610')
WHERE (e.sku > 'n2610')

array("lt"=>'n2610')
WHERE (e.sku < 'n2610')

array("gteq"=>'n2610')
WHERE (e.sku >= 'n2610')

array("moreq"=>'n2610') //a weird, second way to do greater than equal
WHERE (e.sku >= 'n2610')

array("lteq"=>'n2610')
WHERE (e.sku <= 'n2610')

array("finset"=>array('n2610'))
WHERE (find_in_set('n2610',e.sku))

array('from'=>'10','to'=>'20')
WHERE e.sku >= '10' and e.sku <= '20'

ส่วนใหญ่เหล่านี้อธิบายด้วยตนเอง แต่บางคนสมควรได้รับข้อความเสริมพิเศษ

ใน, nin, find_in_set เงื่อนไข in และ nin อนุญาตให้คุณผ่านใน Array of values นั่นคือส่วนค่าของอาร์เรย์ตัวกรองของคุณได้รับอนุญาตให้เป็นอาร์เรย์

array("in"=>array('n2610','ABC123')
WHERE (e.sku in ('n2610','ABC123'))

ไม่เป็นโมฆะคำสำคัญ NULL มีความพิเศษใน SQL รสชาติส่วนใหญ่ โดยทั่วไปแล้วจะไม่เล่นได้ดีกับตัวดำเนินการมาตรฐานความเท่าเทียม (=) การระบุ notnull หรือ null เป็นประเภทตัวกรองของคุณคุณจะได้รับไวยากรณ์ที่ถูกต้องสำหรับการเปรียบเทียบค่า NULL โดยไม่สนใจค่าใด ๆ ที่คุณส่งผ่าน

array("notnull"=>'n2610')
WHERE (e.sku is NOT NULL)

from - to filter นี่เป็นรูปแบบพิเศษอีกรูปแบบหนึ่งที่ละเมิดกฎมาตรฐาน แทนที่จะเป็นอาเรย์องค์ประกอบเดียวคุณต้องระบุอาเรย์สององค์ประกอบ องค์ประกอบหนึ่งมีกุญแจจากองค์ประกอบอื่นมีกุญแจไป ตามที่ระบุไว้ตัวกรองนี้อนุญาตให้คุณสร้างช่วงจาก / ถึงโดยไม่ต้องกังวลเกี่ยวกับสัญลักษณ์ที่มากกว่าและน้อยกว่า

public function testAction
{
        var_dump(
        (string) 
        Mage::getModel('catalog/product')
        ->getCollection()
        ->addFieldToFilter('price',array('from'=>'10','to'=>'20'))
        ->getSelect()
        );                      
}

อัตราผลตอบแทนดังกล่าวข้างต้น

WHERE (_table_price.value >= '10' and _table_price.value <= '20')'

และหรือหรือหรือว่านั้นและและหรือ? ในที่สุดเราก็มาถึงผู้ประกอบการบูลีน เป็นช่วงเวลาที่ไม่ค่อยเกิดขึ้นซึ่งเรากรองโดยแอตทริบิวต์เดียวเท่านั้น โชคดีที่กลุ่มของวีโอไอพีได้รับความคุ้มครอง คุณสามารถโยงการโทรหลายสายเข้าด้วยกันเพื่อ addFieldToFilter เพื่อรับข้อความค้นหา“ AND” จำนวนมาก

function testAction()
{
        echo(
        (string) 
        Mage::getModel('catalog/product')
        ->getCollection()
        ->addFieldToFilter('sku',array('like'=>'a%'))
        ->addFieldToFilter('sku',array('like'=>'b%'))
        ->getSelect()
        );                                  
}

ด้วยการผูกสายเข้าด้วยกันหลายสายข้างต้นเราจะสร้างส่วนคำสั่งที่มีลักษณะดังนี้

WHERE (e.sku like 'a%') AND (e.sku like 'b%')

สำหรับผู้ที่เพิ่งยกมือขึ้นใช่ตัวอย่างข้างต้นจะส่งกลับ 0 ระเบียนเสมอ ไม่มี sku สามารถเริ่มต้นด้วยทั้ง a และ a สิ่งที่เราอาจต้องการคือแบบสอบถาม OR สิ่งนี้นำเราไปสู่อีกมุมมองที่สับสนของพารามิเตอร์ที่สองของ addFieldToFilter

หากคุณต้องการสร้างแบบสอบถาม OR คุณจะต้องผ่าน Array of filter Array เป็นพารามิเตอร์ตัวที่สอง ฉันคิดว่าเป็นการดีที่สุดที่จะกำหนดอาร์เรย์ตัวกรองแต่ละตัวให้กับตัวแปร

public function testAction()
{
        $filter_a = array('like'=>'a%');
        $filter_b = array('like'=>'b%');
}

จากนั้นกำหนดอาร์เรย์ของตัวแปรตัวกรองทั้งหมดของฉัน

public function testAction()
{
        $filter_a = array('like'=>'a%');
        $filter_b = array('like'=>'b%');
        echo(
        (string) 
        Mage::getModel('catalog/product')
        ->getCollection()
        ->addFieldToFilter('sku',array($filter_a,$filter_b))
        ->getSelect()
        );
}

เพื่อประโยชน์ในการมีความชัดเจนนี่คืออาร์เรย์ดังกล่าวของตัวกรองอาร์เรย์

array($filter_a,$filter_b)

สิ่งนี้จะทำให้เราได้ประโยคที่มีลักษณะดังนี้

WHERE (((e.sku like 'a%') or (e.sku like 'b%')))
  1. Varien_Data_Collection :: addFilter
 addFilter($field, $value, $type = 'and')

addFilter()อนุญาตให้กรองฟิลด์เดียวด้วยค่าเดียวและประเภทเท่านั้น $typeสามารถใด ๆ ของ:

  1. "และ" (ค่าเริ่มต้น) - เพิ่ม AND $ field = $ value ในส่วนคำสั่ง WHERE
  2. "หรือ" - เพิ่ม "OR $ field = $ value ให้กับ WHERE clause

ดูรายละเอียดเพิ่มเติม


1
สิ่งนี้ไม่ได้อธิบายอะไรเลย
เฟเบียน Schmengler

มันไม่สมเหตุสมผลเลย ไม่ได้อธิบายความแตกต่างของวิธีการเหล่านี้
Lindar

ดูรายละเอียดเพิ่มเติมwiki.magento.com/display/m1wiki/Using+Magento+1.x+collections
Abdul

2
คำตอบที่อัปเดตของคุณจะถูกคัดลอกส่วนใหญ่มาจากalanstorm.com/magento_collections โปรดอ้างอิงแหล่งที่มาของคุณอย่างน้อย!
เฟเบียน Schmengler
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.