Symfony التطبيق - كيفية إضافة الحقول المحسوبة لدفع الكائنات ؟

StackOverflow https://stackoverflow.com/questions/246400

  •  05-07-2019
  •  | 
  •  

سؤال

ما هي أفضل طريقة العمل مع الحقول المحسوبة من دفع الأجسام ؟

أقول أنا كائن "العميل" أن لديه المقابلة جدول "العملاء" و كل عمود يتوافق مع سمة من وجوه.ما أود القيام به هو:إضافة السمة تحسب "عدد من أوامر الانتهاء" إلى كائن عند استخدامه على عرض ولكن ليس على الآراء B و C.

المحسوبة السمة عدد() من "النظام" كائنات مرتبطة بي "العملاء" كائن عن طريق ID.

ما يمكنني القيام به الآن هو أولا تحديد جميع العملاء الكائنات ، ثم تكراري العد أوامر لجميع من لهم ، ولكن أعتقد أنه فعل ذلك في استعلام واحد من شأنها تحسين الأداء.ولكن أنا لا يمكن بشكل صحيح "ترطيب" بلدي دفع الكائن لأنها لا تحتوي على تعريف الحقل المحسوب(s).

كيف سيكون نهج ذلك ؟

هل كانت مفيدة؟

المحلول

هناك العديد من الخيارات.الأولى هي إنشاء طريقة عرض في DB الخاص بك من شأنها أن تفعل يحسب لك مشابهة جوابي هنا.أفعل هذا من أجل الحالي Symfony مشروع أعمل على سمات للقراءة فقط بالنظر في الجدول هي في الواقع بكثير أوسع من الجدول نفسه.هذا هو بلدي التوصية منذ تجميع الأعمدة (ماكس () ، عدد () ، الخ) هي قراءة فقط على أي حال.

الخيارات الأخرى هي في الحقيقة لبناء هذه الوظيفة إلى النموذج الخاص بك.أنت بالتأكيد يمكن أن تفعل هذا الماء نفسك, لكنه معقد قليلا ، وهنا الخام الخطوات

  1. إضافة أعمدة إلى الجدول الفئة المحمية أعضاء البيانات.
  2. كتابة المناسبة حاصل على واضعي هذه الأعمدة
  3. تجاوز هيدرات طريقة داخل ملء الخاص بك أعمدة جديدة مع البيانات من استعلامات أخرى.تأكد من أن دعوة الوالدين::هيدرات() كما في السطر الأول

بيد أن هذا ليس أفضل بكثير من ما تتحدث عنه بالفعل.سوف لا تزال تحتاج N + 1 الاستفسارات لاسترداد واحد مجموعة السجلات.ومع ذلك ، يمكنك الحصول على الإبداع في الخطوة رقم 3 بحيث N هو عدد الأعمدة المحسوبة ، ليس عدد الصفوف التي يتم إرجاعها.

وثمة خيار آخر هو إنشاء مخصص اختيار الطريقة على الخاص الجدولنظير الدرجة.

  1. هل الخطوات 1 و 2 من فوق.
  2. كتابة SQL مخصص التي سوف الاستعلام يدويا عن طريق دفع::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;
    }
}

قد تكون هناك حلول أخرى للمشكلة ، ولكن هم أبعد حد علمي.حظا سعيدا!

نصائح أخرى

وأنا أفعل هذا في المشروع الآن بتجاوز هيدرات ()، ونظير :: 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 () ولكن لا أستطيع أن أتذكر في هذه اللحظة، ولكن هذا لا يعمل. يمكنك قراءة المزيد عن AddAsColumn () قضايا .

هنا هو ما فعلته من أجل حل هذا دون أي استفسارات إضافية:

المشكلة

تحتاج إلى إضافة مخصص حقل العد نتيجة نموذجية مجموعة استخدامها مع Symfony النداء.ومع ذلك ، كما نعلم ، دفع لا دعم هذا الصندوق.لذلك الحل السهل أن أفعل شيئا مثل هذا في القالب:

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

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

endforeach;

حيث getNumMembers() يدير عدد منفصلة لكل الاستعلام $project الكائن.بالطبع نحن نعلم أن هذا هو كفوء لأنك يمكن أن تفعل الاعتماد على الطاير عن طريق إضافة على أنها العمود الأصلي حدد الاستعلام حفظ استعلام لكل نتيجة عرضها.

لدي عدة صفحات مختلفة لعرض هذه النتيجة, كل ذلك باستخدام معايير مختلفة.حتى كتابة بلدي SQL query string مع شركة تنمية نفط عمان مباشرة سوف يكون الكثير من المتاعب كما يجب أن ندخل في معايير وجوه العبث محاولة لتشكيل سلسلة الاستعلام بناء على ما كان في ذلك!

ذلك ما فعلته في النهاية يتجنب كل ذلك ، السماح دفع الأم رمز العمل مع معايير إنشاء SQL كالمعتاد.

1 - أولا إنشاء [الحصول على/مجموعة]NumMembers() ما يعادل accessor/mutator الأساليب في نموذج الكائن الذي يحصل عودته من doSelect().تذكر accessor لا العد الاستعلام بعد ذلك فقط يحمل قيمته.

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 الأسلوب في الأقران الدرجة:

// 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;         
}

هذا هو.الآن لديك إضافية حقل العد إضافة إلى وجوه الخاص بك دون أن تفعل استعلام منفصل للحصول عليه كما كنت يبصقون النتائج.والعيب الوحيد أن هذا الحل هو أن كنت قد نسخ جميع الوالد رمز لأنك تحتاج إلى إضافة بت في وسط من ذلك.ولكن في حالة بلدي ، بدا هذا مثل صغير وسط لإنقاذ كل تلك الاستفسارات و لا اكتب بلدي SQL سلسلة الاستعلام.

وإضافة سمة "orders_count" لالعملاء، ومن ثم أكتب شيئا من هذا القبيل:

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

ويمكنك استخدام ليس فقط طريقة "حفظ"، ولكن الفكرة يبقى على حاله. للأسف، إدفع لا يدعم أي "سحرية" لمثل هذه المجالات.

وإدفع يبني في الواقع وظيفة التلقائي استنادا إلى اسم الحقل المرتبط. دعونا نقول لديك مخطط من هذا القبيل:

customer:
  id:
  name:
  ...

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

عند بناء النموذج الخاص بك، وجوه العملاء الخاص بك وسوف يكون لها getOrders () طريقة التي سيتم استرداد كافة الطلبات المرتبطة هذا العميل. ثم يمكنك ببساطة استخدام العد ($ العمالء> getOrders ()) للحصول على عدد من الطلبات لهذا العميل.

والجانب السلبي هو هذا سوف تجلب أيضا وترطيب هذه الكائنات الأمر. في معظم RDBMS، والفرق الوحيد بين أداء سحب السجلات أو باستخدام COUNT () هو عرض النطاق الترددي المستخدمة لإرجاع النتائج الموضوعة. إذا كان هذا عرض النطاق الترددي ستكون كبيرة للتطبيق الخاص بك، قد ترغب في إنشاء أسلوب في كائن العملاء الذي يبني الاستعلام 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');
    }
    ...
  }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top