2008-10-29 17 views
5

प्रोपेल ऑब्जेक्ट्स के गणना वाले क्षेत्रों के साथ काम करने का सबसे अच्छा तरीका क्या है?सिम्फनी ऐप - वस्तुओं को प्रक्षेपित करने के लिए गणना किए गए फ़ील्ड को कैसे जोड़ना है?

कहें कि मेरे पास एक ऑब्जेक्ट "ग्राहक" है जिसमें संबंधित तालिका "ग्राहक" है और प्रत्येक कॉलम मेरी ऑब्जेक्ट की विशेषता के अनुरूप है। मैं क्या करना चाहता हूं: व्यू ए पर इसका उपयोग करते समय मेरी ऑब्जेक्ट पर "पूर्ण ऑर्डर की संख्या" जोड़ें, लेकिन दृश्य बी और सी

गणना की गई विशेषता "ऑर्डर" का एक COUNT() है "आईडी के माध्यम से मेरे" ग्राहक "ऑब्जेक्ट से जुड़ी ऑब्जेक्ट्स।

अब मैं क्या कर सकता हूं सबसे पहले सभी ग्राहक वस्तुओं का चयन करना है, फिर उन सभी के लिए ऑर्डर की गणना करें, लेकिन मुझे लगता है कि एक ही क्वेरी में इसे करने से प्रदर्शन में सुधार होगा। लेकिन मैं अपने प्रोपेल ऑब्जेक्ट को ठीक से "हाइड्रेट" नहीं कर सकता क्योंकि इसमें गणना की गई फ़ील्ड की परिभाषा नहीं है।

आप इससे कैसे संपर्क करेंगे?

उत्तर

3

कई विकल्प हैं। सबसे पहले, अपने डीबी में एक दृश्य बनाना है जो आपके जवाब के लिए मायने रखता है, मेरे उत्तर here के समान। मैं यह एक मौजूदा सिम्फनी प्रोजेक्ट के लिए करता हूं जहां मैं काम करता हूं, जहां किसी दिए गए तालिका के लिए केवल पढ़ने योग्य गुण वास्तव में तालिका से अधिक व्यापक होते हैं। कॉलम समूह (अधिकतम(), गिनती(), आदि) के बाद से यह मेरी सिफारिश है, वैसे भी पढ़ा जा सकता है।

अन्य विकल्प वास्तव में इस मॉडल को आपके मॉडल में बनाना है। आप बिल्कुल इस हाइड्रेशन को स्वयं कर सकते हैं, लेकिन यह थोड़ा जटिल है। यहाँ किसी न किसी चरणों

  1. है सुरक्षित डेटा सदस्यों के रूप में वर्ग अपने तालिका में कॉलम जोड़ने।
  2. इन कॉलम के लिए उपयुक्त गेटर्स और सेटर्स लिखें
  3. हाइड्रेट विधि को ओवरराइड करें और भीतर, अन्य प्रश्नों से डेटा के साथ अपने नए कॉलम को पॉप्युलेट करें। माता-पिता :: हाइड्रेट() को पहली पंक्ति

के रूप में कॉल करना सुनिश्चित करें, हालांकि, यह पहले से ही आप जो बात कर रहे हैं उससे कहीं बेहतर नहीं है। आपको अभी भी एक रिकॉर्ड रिकॉर्ड पुनर्प्राप्त करने के लिए एन + 1 प्रश्नों की आवश्यकता होगी। हालांकि, आप चरण # 3 में रचनात्मक हो सकते हैं ताकि एन गणना की गई कॉलम की संख्या है, न कि पंक्तियों की संख्या लौटा दी गई है।

एक और विकल्प है अपने तालिका पीयर क्लास पर कस्टम चयन विधि बनाना।

  1. ऊपर से चरण 1 और 2 करें।
  2. कस्टम एसक्यूएल लिखें जो आप Propel :: getConnection() प्रक्रिया के माध्यम से मैन्युअल रूप से क्वेरी करेंगे।
  3. परिणाम सेट पर पुन: स्थापित करके मैन्युअल रूप से डेटासेट बनाएं, और इस बिंदु पर कस्टम हाइड्रेशन को संभालें क्योंकि DoSelect प्रक्रियाओं द्वारा उपयोग किए जाने पर हाइड्रेशन को तोड़ना नहीं है।

यहाँ इस दृष्टिकोण का एक उदाहरण

<?php 

class TablePeer extends BaseTablePeer 
{ 
    public static function selectWithCalculatedColumns() 
    { 
     // Do our custom selection, still using propel's column data constants 
     $sql = " 
      SELECT " . implode(', ', self::getFieldNames(BasePeer::TYPE_COLNAME)) . " 
       , count(" . JoinedTablePeer::ID . ") AS calc_col 
       FROM " . self::TABLE_NAME . " 
       LEFT JOIN " . JoinedTablePeer::TABLE_NAME . " 
       ON " . JoinedTablePeer::ID . " = " . self::FKEY_COLUMN 
     ; 

     // Get the result set 
     $conn = Propel::getConnection(); 
     $stmt = $conn->prepareStatement($sql); 
     $rs = $stmt->executeQuery(array(), ResultSet::FETCHMODE_NUM); 

     // Create an empty rowset 
     $rowset = array(); 

     // Iterate over the result set 
     while ($rs->next()) 
     { 
      // Create each row individually 
      $row = new Table(); 
      $startcol = $row->hydrate($rs); 

      // Use our custom setter to populate the new column 
      $row->setCalcCol($row->get($startcol)); 
      $rowset[] = $row; 
     } 
     return $rowset; 
    } 
} 

आपकी समस्या का अन्य समाधान हो सकता है, लेकिन वे अपने ज्ञान से परे हैं। शुभकामनाएँ!

+0

उस उत्तर के लिए धन्यवाद - इससे मुझे एक अलग समस्या हल करने में मदद मिली है! –

0

एक ग्राहक के लिए एक विशेषता "orders_count" जोड़ें, और उसके बाद कुछ इस तरह लिखना:

class Order { 
... 
    public function save($conn = null) { 
    $customer = $this->getCustomer(); 
    $customer->setOrdersCount($customer->getOrdersCount() + 1); 
    $custoner->save(); 
    parent::save(); 
    } 
... 
}

आप न केवल "बचाने" विधि का उपयोग कर सकते हैं, लेकिन यह विचार ही रहता है। दुर्भाग्यवश, प्रोपेल ऐसे क्षेत्रों के लिए किसी भी "जादू" का समर्थन नहीं करता है।

+0

मैं भी इस पर विचार कर रहा हूं - धन्यवाद! –

0

प्रोपेल वास्तव में लिंक किए गए फ़ील्ड के नाम के आधार पर एक स्वचालित फ़ंक्शन बनाता है। मान लीजिए कि आप इस तरह एक स्कीमा करते हैं:

customer: 
    id: 
    name: 
    ... 

order: 
    id: 
    customer_id: # links to customer table automagically 
    completed: { type: boolean, default false } 
    ... 

जब आप अपने मॉडल बनाने के लिए, अपने ग्राहकों की वस्तु एक विधि getOrders() कि है कि ग्राहक के साथ जुड़े सभी आदेशों को पुनः प्राप्त होगा। उसके बाद आप उस ग्राहक के ऑर्डर की संख्या प्राप्त करने के लिए केवल गिनती ($ ग्राहक-> getOrders()) का उपयोग कर सकते हैं।

नकारात्मकता यह है कि यह उन ऑर्डर ऑब्जेक्ट्स को भी लाएगा और हाइड्रेट करेगा। अधिकांश आरडीबीएमएस पर, रिकॉर्ड खींचने या COUNT() का उपयोग करने के बीच एकमात्र प्रदर्शन अंतर बैंडविड्थ परिणाम सेट को वापस करने के लिए उपयोग किया जाता है।

// in lib/model/Customer.php 
    class Customer extends BaseCustomer 
    { 
    public function CountOrders() 
    { 
     $connection = Propel::getConnection(); 
     $query = "SELECT COUNT(*) AS count FROM %s WHERE customer_id='%s'"; 
     $statement = $connection->prepareStatement(sprintf($query, CustomerPeer::TABLE_NAME, $this->getId()); 
     $resultset = $statement->executeQuery(); 
     $resultset->next(); 
     return $resultset->getInt('count'); 
    } 
    ... 
    } 
+0

मैंने सोचा कि हाइड्रेटिंग मेरे प्रदर्शन को नुकसान पहुंचाती है। हालांकि कस्टम COUNT फ़ंक्शन के साथ टिप के लिए धन्यवाद! –

+0

इस बात पर निर्भर करता है कि हम कितने रिकॉर्ड के बारे में बात कर रहे हैं, लेकिन आप सही हैं। मैं चीजों के डेटाबेस बैकएंड पक्ष पर और बात कर रहा था। –

1

मैं हाइड्रेट अधिभावी द्वारा अब एक परियोजना में यह कर रहा हूं: कि बैंडविड्थ आपके आवेदन के लिए महत्वपूर्ण होगा, तो आप उस COUNT() क्वेरी मैन्युअल क्रियोल का उपयोग कर बनाता है ग्राहक वस्तु में एक विधि बनाने के लिए चाहते हो सकता है() और सहकर्मी :: addSelectColumns() PostGIS क्षेत्रों तक पहुँचने के लिए:

// in peer 
public static function locationAsEWKTColumnIndex() 
{ 
    return GeographyPeer::NUM_COLUMNS - GeographyPeer::NUM_LAZY_LOAD_COLUMNS; 
} 

public static function polygonAsEWKTColumnIndex() 
{ 
    return GeographyPeer::NUM_COLUMNS - GeographyPeer::NUM_LAZY_LOAD_COLUMNS + 1; 
} 

public static function addSelectColumns(Criteria $criteria) 
{ 
    parent::addSelectColumns($criteria); 
    $criteria->addAsColumn("locationAsEWKT", "AsEWKT(" . GeographyPeer::LOCATION . ")"); 
    $criteria->addAsColumn("polygonAsEWKT", "AsEWKT(" . GeographyPeer::POLYGON . ")"); 
} 
// in object 
public function hydrate($row, $startcol = 0, $rehydrate = false) 
{ 
    $r = parent::hydrate($row, $startcol, $rehydrate); 
    if ($row[GeographyPeer::locationAsEWKTColumnIndex()]) // load GIS info from DB IFF the location field is populated. NOTE: These fields are either both NULL or both NOT NULL, so this IF is OK 
    { 
     $this->location_ = GeoPoint::PointFromEWKT($row[GeographyPeer::locationAsEWKTColumnIndex()]); // load gis data from extra select columns See GeographyPeer::addSelectColumns(). 
     $this->polygon_ = GeoMultiPolygon::MultiPolygonFromEWKT($row[GeographyPeer::polygonAsEWKTColumnIndex()]); // load gis data from extra select columns See GeographyPeer::addSelectColumns(). 
    } 
    return $r; 
} 

वहाँ AddAsColumn के साथ कुछ नासमझ() है, लेकिन मैं इस समय याद नहीं कर सकते, लेकिन यह काम करता है। आप read more about the AddAsColumn() issues कर सकते हैं।

1

यहाँ क्या मैं किसी भी अतिरिक्त प्रश्नों के बिना इस को हल करने के लिए किया था है:

समस्या

एक विशिष्ट परिणाम Symfony पेजर के साथ प्रयोग किया सेट करने के लिए कस्टम COUNT क्षेत्र को जोड़ने के लिए की आवश्यकता। हालांकि, जैसा कि हम जानते हैं, प्रोपेल इस बॉक्स का समर्थन नहीं करता है। तो आसान समाधान सिर्फ टेम्पलेट में कुछ इस तरह करना है:

foreach ($pager->getResults() as $project): 

echo $project->getName() . ' and ' . $project->getNumMembers() 

endforeach; 

कहाँ getNumMembers() प्रत्येक $project वस्तु के लिए एक अलग COUNT क्वेरी चलाता है। बेशक, हम जानते हैं कि यह काफी अक्षम है क्योंकि आप मूल चयन क्वेरी में कॉलम के रूप में इसे जोड़कर फ्लाई पर COUNT कर सकते हैं, प्रत्येक परिणाम प्रदर्शित करने के लिए एक क्वेरी सहेज सकते हैं।

मेरे पास इस परिणाम सेट को प्रदर्शित करने वाले कई अलग-अलग पृष्ठ थे, सभी अलग मानदंडों का उपयोग करते हुए। तो पीडीओ के साथ अपनी खुद की एसक्यूएल क्वेरी स्ट्रिंग को सीधे लिखना बहुत मुश्किल होगा क्योंकि मुझे इसमें जो कुछ भी था, उसके आधार पर क्वेरी स्ट्रिंग बनाने की कोशिश करने के आसपास मुझे मानदंड वस्तु और गड़बड़ में जाना होगा!

तो, मैंने अंत में जो किया वह सब से बचाता है, प्रोपेल के देशी कोड को मानदंड के साथ काम करने और सामान्य रूप से एसक्यूएल बनाने की अनुमति देता है।

1 - पहले मॉडल ऑब्जेक्ट में [get/set] न्यूममेम्बर() समकक्ष एक्सेसर/म्यूटेटर विधियों को बनाएं जो DoSelect() द्वारा लौट रहे हैं। याद रखें, एक्सेसर अब COUNT क्वेरी नहीं करता है, यह केवल उसका मान रखता है।

2 - सहकर्मी वर्ग में जाकर माता-पिता doSelect() विधि ओवरराइड और इसे से सभी कोड को कॉपी बिल्कुल के रूप में यह

3 - इस बिट निकालें क्योंकि getMixerPreSelectHook आधार सहकर्मी के लिए एक निजी तरीका है (या अपने सहकर्मी में कॉपी यदि आपको आवश्यकता):

// symfony_behaviors behavior 
foreach (sfMixer::getCallables(self::getMixerPreSelectHook(__FUNCTION__)) as $sf_hook) 
{ 
    call_user_func($sf_hook, 'BaseTsProjectPeer', $criteria, $con); 
} 

4 - अब आप अपने साथियों के वर्ग में doSelect विधि करने के लिए अपने कस्टम COUNT क्षेत्र जोड़ें:

// copied into ProjectPeer - overrides BaseProjectPeer::doSelectJoinUser() 
public static function doSelectJoinUser(Criteria $criteria, ...) 
{ 
    // copied from parent method, along with everything else 
    ProjectPeer::addSelectColumns($criteria); 
    $startcol = (ProjectPeer::NUM_COLUMNS - ProjectPeer::NUM_LAZY_LOAD_COLUMNS); 
    UserPeer::addSelectColumns($criteria); 

    // now add our custom COUNT column after all other columns have been added 
    // so as to not screw up Propel's position matching system when hydrating 
    // the Project and User objects. 
    $criteria->addSelectColumn('COUNT(' . ProjectMemberPeer::ID . ')'); 

    // now add the GROUP BY clause to count members by project 
    $criteria->addGroupByColumn(self::ID); 

    // more parent code 

    ... 

    // until we get to this bit inside the hydrating loop: 

    $obj1 = new $cls(); 
    $obj1->hydrate($row); 

    // AND...hydrate our custom COUNT property (the last column) 
    $obj1->setNumMembers($row[count($row) - 1]); 

    // more code copied from parent 

    ... 

    return $results;   
} 

यह है कि। अब आपके पास परिणाम को थूकने के लिए अलग-अलग क्वेरी करने के बिना आपके ऑब्जेक्ट में अतिरिक्त COUNT फ़ील्ड जोड़ा गया है। इस समाधान में एकमात्र कमी यह है कि आपको सभी मूल कोड कॉपी करना होगा क्योंकि आपको इसके बीच में बिट्स जोड़ने की आवश्यकता है। लेकिन मेरी स्थिति में, यह उन सभी प्रश्नों को सहेजने के लिए एक छोटी समझौता की तरह लग रहा था और अपनी खुद की एसक्यूएल क्वेरी स्ट्रिंग नहीं लिख रहा था।

संबंधित मुद्दे

 संबंधित मुद्दे