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

is there plans for polymorphic models?

I cannot find any example or anything in the documentation about polymorphic models in phalcon. As far as I understand, Phalcon and Zephir boast of being OOP PHP tools. But in the other hand, the models (at least, maybe other components as well) constrain the user to use ifs/switchs instead of polymorphism. Maybe it is possible to achieve with postgres(it supports polymorphism in the database) , but what about mysql? Propel (and maybe Doctrine?) allows polymorphism based on some columns ("type" for example). If this feature would exist, Phalcon users would be able to write true OOP code. In PHP ifs are optional for procedural code lovers; but with Phalcon models, ifs are still a must! Or maybe the ORM can be renamed to SRM (struct-relational mapper) to avoid the confusions :)

Im not sure what you mean, can you show any example ? What you want to achievie etc ?



2.8k
edited Aug '16

Sure, polymorphism is a pillar of OOP. Without it, we cannot say we are using OOP.

Phalcon ORM achieves encapsulation. And Inheritance? Well, no. It just extend from a generic Model class, but never from indivisual classes based on DB data (that would be useful for user cases)

I refered to Propel, I was expecting readers to search for it. But ok, take a look here... https://propelorm.org/Propel/documentation/09-inheritance.html For the moment, I cannot or I don't know how to achieve this in Phalcon. So I have to use procedural workarounds, for example:

<?php
class Demo extends Model {
function getPrice() {
    if ($this->type === self::TYPE_STATIC)
        return $this->price;
    if($this->type === self::TYPE_WEEKLY)
        return $this->weeks * $this->price;
}
}

instead of:

<?php
class DemoWeekly extends Demo {
function getPrice() {
        return $this->weeks * $this->price;
}
}

As you can see, OOP code (with inheritance, and polymorphism) is easier to read than the procedural version.

edited Aug '16

What you mean ? I already have something you posted :) Like one model extending other model etc.

Also imho for demo weekly you should just have stored calculated value under database in my opinion. Or calculate it in database to avoid method calls and other things(like selecting columns from DemoWeekly then your getPrice() wouldn't be used at all beacause phalcon is not using your model objects when selecting columns.



2.8k

At least, it is not documented in this page: https://docs.phalcon.io/en/latest/reference/models.html can you share the documentation for this feature?

Well, yes and not, it depends, it is similar to virtual columns in mariadb/mysql. It is an expression, not a materialized value. But that is not the main point of this discussion. It was just an example. You can also see examples of inheritance in class Console extends \Phalcon\Application (but this doesn't store anything in DB).

Please check the Propel docummentation I provided, to see what means "inheritance" for models, notice that a PHP object is not a DB row in all the cases. There is a basic table and other tables with extra fields.

Of course, this is a feature for those who have to use MySQL, as I said PostgreSQL and other ORDBMS don't benefit so much from this. Take a look in the documentation: https://www.postgresql.org/docs/9.1/static/ddl-inherit.html So, in this particular case, we don't need a column "type" and other complementary tables. I guess that inheriting from a parent class can make the trick. But in MySQL we still need some solution.

What do you think about this?

But same in phalcon - you can have one model with a few fields, and other model extending it with more fields and more columns in database - no problem with this at all. Why document such obvious things ?



2.8k
edited Aug '16

It seems you are not reading the propel documentation: https://propelorm.org/Propel/documentation/09-inheritance.html

Notice there is a column class_key, if you do a $o->find(); it may return objects of different classes, for example: $book->find() = [$essey,$comic, $biography], in what way is this obvious? How is it obvious where to specify to Phalcon the polimorphic column? Where should I specify this? Can you provide an example? something like

public function initialize()
{
    $this->setClassKey('class_key', [1 => "Book\Essay", 2 => "Book\Comic"]);
}

So if i read {id:n, type:1, ... } I will get a Essay object, and not a generic book. The same for {id:n, type:2, ... } = Comic object

And , how to specify the name of the additional tables to compound the object, also in the initialize? The find method in Comic will have to join the book and book_comic table in order to read the whole object.

Please read the documentation of Propel, before answering again

First of all find method in phalcon is not joining anything at all.

I mean you can currently do something like this:

class A extends Phalcon/Mvc/Model
{
    public $id;
    public $name;
}
class B extends A 
{
public $price;
}

This will just work as it should be. But this things you are writing looks like really more complex, for me active record pattern shouldn't do such a stuff. If you need to select rows from few tables then as far as i know you need to use raw sql for this and union select.



2.8k

Yes, I know it is possible to achieve that, but that implies 2 different tables that are not related at all in the database, just we humans know there is a relationship. But that doesn't achieve the desired result, like that propel can do:

<?php
$books = BookQuery::create()->find();
foreach ($books as $book) {
  echo get_class($book) . ': ' . $book->getTitle() . "\n";
}
// Book: War And Peace
// Essay: On the Duty of Civil Disobedience
// Comic: Little Nemo In Slumberland
// Novel: Harry Potter

Book in that example just refers to one table in mysql. Maybe Phalcon team is not aware of this kind of features or maybe it is not really interested on it.

Your proposal works good for postgresql, where we have tables like

CREATE TABLE cities (
    name            text,
    population      float,
    altitude        int     -- in feet
);

CREATE TABLE capitals (
    state           char(2)
) INHERITS (cities);

where capitals.name is stored in cities but capitals.state in other place. But not perfect, because we couldn't retrieve all the columns of the capital while querying from cities.

Nobody said that an ORM is an easy and simple task :) it is the most leaked abstraction ever (maybe impossible to abstract?). But it is still possible to map relations to objects (inheritance and polymorphism is not optional in OOP, they are the key features)