We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Models - Abstract base with concrete classes? PHP implementation of model::find()?

I have an abstract class (well it should be, but right now it's a public class), Widget - there are many types of widget (WidgetOne, WidgetTwo etc.) that necesitate multiple models in Phalcon, there's quite a lot of commanality hence the abstract class.

So far, not a probem. The problem comes with working with the ORM - I can call:

Widget::find()

(hence not going with abstract in reality) which returns me all the widgets I want, but they are all obviously of type Widget, not what they are in reality, WidgetOne, WidgetTwo etc.

As far as I can see Phalcon has no way of handling this.

Does anyone have a PHP implementation of Phalcon\Mvc\Model::find() ? If I had that then my Widget model can provide it's own find, findFirst etc. as the Widget table has a field: type which I can use to get the actual Widget subclass from.

Thanks,

Karl



33.8k

And why don't you create your own/override find()? Just call it findWidget(), and get which Widget class is calling it (get_called_class() I think); then, do a $widgetClass::find(..) and return the result, so you don't need to use the field. You could do the same creating a constant class that says which Widget are you using for the function ($widgetClass::WIDGET_CLASS) in every subwidget as opposite to the previous option. Or you could just use your field to assign correctly $widgetClass.

But I'm not sure if it is what you want.

Could you try to declare in your absctract class get method with something like this:

<?php

  abstract class Widget extends \Phalcon\Mvc\Model
  {
      /**
       * Default order
       * @type string
       */
      const DEFAULT_ORDER = null;

      /**
       * Thw scope that should be implicitly applied to all queries for this model
       * @type string
       */
      const DEFAULT_SCOPE = null;

      /**
       * Default limit
       * @type int
       */
      const DEFAULT_LIMIT = 30;

      public static function get($condition, $params, $order = null, $limit = null)
      {
          $condition = vsprintf($condition, $params);

          if (static::DEFAULT_SCOPE) {
              $condition = sprintf('%s %s %s', $condition, $condition ? 'AND' : '', static::DEFAULT_SCOPE);
              $condition = trim($condition);
          }

          $parameters = [$condition];

          if ($order) {
              $parameters['order'] = $order;
          } elseif (static::DEFAULT_ORDER) {
              $parameters['order'] = static::DEFAULT_ORDER;
          }

          if ($limit) {
              $parameters['limit'] = $limit;
          } elseif (static::DEFAULT_LIMIT) {
              $parameters['limit'] = static::DEFAULT_LIMIT;
          }

          return self::find($parameters);
      }
  }

And your child model:

<?php

class WidgetOne extends Widget
{
  // ...
    /** {@inheritdoc }*/
    const DEFAULT_ORDER = 'created_at';

    /** {@inheritdoc }*/
    const DEFAULT_SCOPE = 'type=one';
}

I hope has helped you

I want to avoid specifically requesting the type from Widget, by doing something like findWidget($type) - as I could in effect do that now, by just calling WidgetOne::find() - but that means every time a new Widget type is added, I need to modify lots of code to cope with it. It also means I have to mash several result sets together to sort them in the correct order. Hence wanting to just have the Widgets be of the correct type when returned from their parent classes find() method.

Same sort of isuse, it's not a problem when I call WidgetOne directly etc. I've already solved that side of it. It's more when I need to fetch a list of widgets in bulk, which could be of any type e.g.

Last 50 widgets ordered - could be any type of widget, but they'll all come back from Widget::find() right now with a type of Widget, rather than WidgetOne, WidgetTwo etc. Right now I'd have to get the ID + type of the last 50, then do WidgetOne::find() passing it an array of IDs, same for every other type of widget in the last 50 - then sort them in to the correct order.

Looks like someone has done a pull for 2.0.0 that could solve this, https://github.com/vpg/cphalcon/commit/325a18fa710932a90b98b6b511d1aaa16799dc55 so I may have to wait until it is merged in.



1.7k
Accepted
answer

Using 2.0.0 with the Late State Binding patch and it's working perfect for what I need.