Search code examples
cakephpmodelscakephp-3.0

Why a virtual property getter should not be called in cakePHP 3?


in a ctp, I display several virtual properties from 2 entities, computed by getters. It's ok for one of the both entities but for the other, none of the properties getters are never called.

Here is an excerpt of the ctp:

<table>
    ....
    <td><?= $agTheme->agthemelanguages_wc_string ?></td>
    <td><?= $agPoi->paragraph_length_string ?></td>
    <td><?= $agPoi->images_string ?></td>
    <td><?= $agPoi->audios_string ?></td>
    ....
</table>

It's ok for AgTheme's property but no getter of the Agpoi's properties is called.

Agtheme model is:

<?php
namespace App\Model\Entity;

use Cake\ORM\Entity;
use Cake\Collection\Collection;
use Cake\Core\Configure;
use Cake\Log\Log;
use Cake\ORM\TableRegistry;

class Agtheme extends Entity {

    protected $_accessible = [
        'site_id' => true,
        'site' => true,
        'name' => true,
        'directory_name' => true,
        'comment' => true,
        'image_0' => true,
        'imgpathname0' => true,
        'imgfile0' => true,
        'mobile_theme' => true,
        'agthemelanguages' => true,
        'poi_by_tag' => true,
        'poi_to_map' => true,
        'unlock_mode' => true,
        'unlock_code' => true,
        'published' => true,
        'approved' => true,
    ];


    protected function _getAgthemelanguagesWcString() {

        if (isset($this->_properties['agthemelanguages_wc_string'])) {
            return $this->_properties['agthemelanguages_wc_string'];
        }

        if (empty($this->agthemelanguages)) {
            return '';
        }

        $agthemelanguages = new Collection($this->agthemelanguages);

        $str = $agthemelanguages->reduce(function ($string, $agthemelanguage) {
            return $string . $agthemelanguage->language->name_fr . ' :<br/>';
        }, '');

        return trim($str, '<br/>');

    }

    function _getImgpathname0() {

        if (isset($this->_properties['imgpathname0'])) {
            return $this->_properties['imgpathname0'];
        }

        if (!isset($this->_properties['id']) || empty($this->image_0) || !isset($this->_properties['directory_name'])) {
            return '';
        }

        $img_path = SITES_CONTENT_URL . $this->_properties['directory_name'] .'/imgTheme_0.' . $this->_properties['image_0'];

        return $img_path;
    }
}

And Agpoi model is (Agpoi.php):

<?php
namespace App\Model\Entity;

use Cake\ORM\Entity;
use Cake\Collection\Collection;

class Agpoi extends Entity
{

    protected $_accessible = [
        'name' => true,
        'latitude' => true,
        'longitude' => true,
        'bearing' => true,
        'preload' => true,
        'agtheme' => true,
        'agpoiaudios' => true,
        'agpoiimages' => true,
        'agpoitextes' => true,
        'agpoiwaypoints' => true,
    ];

    protected function _getImagesString()
    {
        if (isset($this->_properties['images_string'])) {
            return $this->_properties['images_string'];
        }

        if (empty($this->agpoiimages)) {
            return '';
        }

        $agpoiimages = new Collection($this->agpoiimages);

        $str = $agpoiimages->reduce(function ($string, $agpoiimage)
        {
            return $string . $agpoiimage->image . ', ';
        }, '');

        return trim($str, ', ');
    }

    protected function _getAudiosString()
    {
        if (isset($this->_properties['audios_string'])) {
            return $this->_properties['audios_string'];
        }
        if (empty($this->agpoiaudios)) {
            return '';
        }

        $agpoiaudios = new Collection($this->agpoiaudios);

        $str = $agpoiaudios->reduce(function ($string, $agpoiaudio)
        {
            return $string . $agpoiaudio->filename . ', ';
        }, '');

        return trim($str, ', ');
    }

    protected function _getParagraphLengthString() {

        if (isset($this->_properties['paragraph_length_string'])) {
            return $this->_properties['paragraph_length_string'];
        }

        if (empty($this->agpoitextes)) {
            return '';
        }

        $agpoitextes = new Collection($this->agpoitextes);

        $str = $agpoitextes->reduce(function ($string, $agpoitexte) {
            return $string . str_word_cound($agpoitexte->paragraph) . __(" mots<br/>");
        }, '');

        return trim($str, '<br/>');

    }
}

Unfortunately, I doubt that the problem could come from the code above, I better think about of a stupid mistake somewhere else but I really wonder what could cause such problem and here is my question:

Could anyone tell me what allow cake to look for a getter? What could prevent cake to call a getter??

I guess the answer is not obvious for the last question but any hint would be appreciated...

EDIT

@ndm debug(get_class($agPoi)); 'Cake\ORM\Entity'

whereas debug(get_class($agTheme)); 'App\Model\Entity\Agtheme'

so obviously Agpoi is not well defined but I don't see why...

EDIT 2 Table is (AgpoisTable.php):

<?php
namespace App\Model\Table;

use Cake\ORM\Query;
use Cake\ORM\Table;
use Cake\Validation\Validator;

/**
 * Agpois Model
 */
class AgpoisTable extends Table {

/**
 * Initialize method
 *
 * @param array $config The configuration for the Table.
 * @return void
 */
    public function initialize(array $config) {
        $this->table('agpois');
        $this->displayField('id');
        $this->primaryKey('id');

        $this->belongsToMany('Agthemes', [
            'foreignKey' => 'agpoi_id',
            'targetForeignKey' => 'agtheme_id',
            'joinTable' => 'agthemes_agpois',
            'dependent' => true,
        ]);

        $this->hasMany('Agpoiaudios', [
            'dependent' => true,
            'foreignKey' => 'agpoi_id',
        ]);

        $this->hasMany('Agpoiimages', [
            'dependent' => true,
            'foreignKey' => 'agpoi_id',
        ]);

        $this->hasMany('Agpoitextes', [
            'dependent' => true,
            'foreignKey' => 'agpoi_id',
        ]);

        $this->hasMany('Agpoiwaypoints', [
            'dependent' => true,
            'foreignKey' => 'agpoi_id',
        ]);


    }

    public function validationDefault(Validator $validator) {
        $validator
            ....
        return $validator;
    }
}

I also made a search for 'agpoi' everywhere in case.... no unexpected other agpoi classname or something like that.

debug($this->Agpois);

object(App\Model\Table\AgpoisTable) {

    'registryAlias' => 'Agpois',
    'table' => 'agpois',
    'alias' => 'Agpois',
    'entityClass' => '\Cake\ORM\Entity',
    'associations' => [
        (int) 0 => 'agthemes',
        (int) 1 => 'agpoiaudios',
        (int) 2 => 'agpoiimages',
        (int) 3 => 'agpoitextes',
        (int) 4 => 'agpoiwaypoints',
        (int) 5 => 'agthemesagpois'
    ],
    'behaviors' => [],
    'defaultConnection' => 'default',
    'connectionName' => 'default'
}

so always the problem of entityClass of course.


Solution

  • The problem here seems to be name of the table class, respetively the name of the entity class, probably depends on your perspective.

    By default, the name of the corresponding entity class is guessed by using the singular table class name without the trailing Table, however Agpois doesn't inflect to Agpoi as you might have assumed, but just Agpois, and so it's looking for the class App\Model\Entity\Agpois, which doesn't exist, hence the fallback to the default entity base class.

    I'm not sure what Agpois means, and which language it stems from, but it's certainly not english, and thus such problems aren't unexpected, as the Inflector is ment to handle english words only.

    tl;dr

    If you must use the name Apgois, then you'll have to either define the name of the entity class manually in your tables initialize() method:

    public function initialize(array $config)
    {
        // ...
        $this->entityClass('Apgoi');
        // ...
    }
    

    or configure the inflector so that it can handle that word:

    Inflector::rules('irregular', ['apgoi' => 'apgois']);
    

    or even rename the entity class to Apgois if that is applicable and doesn't cause any naming conflicts.

    See also