Respect / Relational

A fluent, intuitive ORM for any relational database engine
http://respect.github.io/Relational
Other
243 stars 32 forks source link

Problem with protected properties in Entity Class #56

Closed diegocpires closed 10 years ago

diegocpires commented 10 years ago

Hi!

Im working with Entity Class using for reference this manual section https://github.com/Respect/Relational#entity-classes.

My code is working ok, except one item.

When i use protected properties in classes, this properties not persist in the database. I'm looking in the source code of Relational and i really not understand why this "columns" are not persist. (In time, i'm using magic methods for call, get, isset and set).

Anyone works with protected properties and Entity Classes to help me in a solution?

augustohp commented 10 years ago

I don't believe protected nor private visibility is supported, what do you think of that and how you solved your problem?

diegocpires commented 10 years ago

Pascutti,

Now i don't have much time to change the "core" of Relational, anyway i changed the properties to public and, go horse. =P

When i deploy the first version of the system, i look a solution for this.

alganet commented 10 years ago

@diegocpires private properties are accessible only using methods from convention styles. It cannot read from set and get.

This should work:

<?php

namespace MyApplication;

class Post
{
    public $id;
    private $name;

    public function setName($name)
    {
        $this->name = $name;
    }

    public function getName()
    {
        return $name;
    }
}

$mapper = new \Respect\Relational\Mapper($myPDOInstance);
$mapper->entityNamespace = '\\MyApplication\\';

$newPost = new Post;
$newPost->setName("Example Post");

$mapper->persist($newPost);
$mapper->flush();

The id property has a problem, though. The current implementation requires it to be public in order to work. It shouldn't be hard to change it here and here to use the inferSet method that figures out which setter to use.

I'm reopening this, it may be a quick fix.

diegocpires commented 10 years ago

@alganet Weird... I'm using magic method __call, to not declare every getter and setter.

But, declaring the getters and setters still not working! The registry in database is created, but values are null.

My "GenericMapper"

<?php
namespace ProCorpo\CRM\Mapper;

use Respect\Relational\Sql;
use Respect\Config\Container;
use Respect\Relational\Db;

abstract class GenericMapper {

    public $conexao;
    protected $c;
    protected $db;
    protected static $instances;

    public function __construct() {
        $this->c = new Container(CONFIG_DIR . '/config.ini');
        $this->conexao = $this->c->mapper;
        $this->conexao->entityNamespace =  '\\ProCorpo\\CRM\\Model\\';
        $this->db = new DB($this->c->pdo);
    }

My "GenericModel"

<?php
namespace ProCorpo\CRM\Model;

use ProCorpo\CRM\Helper\Formatters as f;

abstract class GenericModel {

    protected $mapper;

    public function __construct()
    {
        $this->mapper = \ProCorpo\CRM\Mapper\Factory::getInstance($this->nomeEntidade);
    }

    public function __call($nome, $argumentos)
    {
        if(substr($nome, 0, 3) == "get")
        {
            $nomePropriedade = substr($nome, 3);
            return $this->getMagico($nomePropriedade, $argumentos);
        }
        if(substr($nome, 0, 3) == "set")
        {
            $nomePropriedade = substr($nome, 3);
            return $this->setMagico($nomePropriedade, $argumentos);
        }

        throw new Exception("Método não existe", 1);

    }

    protected function getMagico($nome, $argumentos)
    {
        $nomePropriedade = \ProCorpo\CRM\Helper\Formatters::fromCamelCase($nome);
        if(property_exists(self::nomeClasse(), $nomePropriedade)) {
            return $this->{$nomePropriedade};
        }
    }

My "BancoModel"

<?php
namespace ProCorpo\CRM\Model;

use ProCorpo\CRM\Helper\Formatters as f;

class Banco extends GenericModel {

    protected $unidade_id, $nome, $valor; 
    public $id;

    public function setNome($nome) {
        $this->nome = $nome;
    }

    public function getNome() {
        return $this->nome;
    }

I'll look at these two points you mentioned to try to fix it.

diegocpires commented 10 years ago

@alganet @augustohp I made changes in method extractColums for tests. Aparently change only this method works, at least in my case works. I'm not sure this is correct or this is the best approach , but, again, in my case works.

I'm catching only protected and public properties.

protected function extractColumns($entity, Collection $collection)
{
    $primaryName = $this->getStyle()->identifier($collection->getName());

    $nameClass = get_class($entity);
    $reflect = new \ReflectionClass($nameClass);
    $props   = $reflect->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED);
    $cols = array();
    foreach($props as $prop) {
        $nameCamelCase = "get".f::toCamelCase($prop->name);

        if(is_callable(array($entity, $nameCamelCase))) {
            $valueField = $entity->$nameCamelCase();
        } else {
            $valueField = $entity->{$prop->name};
        }
        $cols[$prop->name] = $valueField;
    }

    foreach ($cols as &$c) {
        if (is_object($c)) {
            $c = $c->{$primaryName};
        }
    }

    return $cols;
}
felipecwb commented 10 years ago

@diegocpires @alganet what do you think in use \ReflectionProperty::setAccessible()?

protected function extractColumns($entity, Collection $collection)
{
    $primaryName = $this->getStyle()->identifier($collection->getName());

    $reflect = new \ReflectionClass($entity);
    $props   = $reflect->getProperties(
        \ReflectionProperty::IS_PUBLIC 
        | \ReflectionProperty::IS_PROTECTED
        | \ReflectionProperty::IS_PRIVATE
    );

    $cols = array();
    foreach($props as $prop) {
        if (!$prop->isPublic()) $prop->setAccessible(true);

        $value = $prop->getValue($entity);
        $cols[$prop->name] = is_object($value) ? $value->{$primaryName} : $value;
    }

    return $cols;
}

now in Mapper::tryHydration(); you can use the ReflectionProperty::setValue($instance, $value); To you use private property like PDOStatment::fetch(); do in fetch class mode.

felipecwb commented 10 years ago

Sorry for a lot of commits! I was working on two projects and I wrong terminal. So, I rebase and pull the relational instead of other.

But the last commit is right. https://github.com/felipecwb/Relational/commit/618637be27aad4c069bb9c745cfd9e94ea2df4c2

williamespindola commented 10 years ago

@diegocpires any more about that? This issue can be closed?

diegocpires commented 10 years ago

For me is perfect.

Thanks to all for the support. And for @felipecwb for the wonderful job. =D