0

I baked a model into a plugin, but it fails to load.

What I did:

  1. Baked a model

    bin/cake bake model History --connection data --plugin Data --no-rules --no-validation --table elements_history
    

One moment while associations are detected.

Baking table class for History...

Creating file /var/www/app/plugins/Data/src/Model/Table/HistoryTable.php Wrote /var/www/app/plugins/Data/src/Model/Table/HistoryTable.php

Baking entity class for History...

Creating file /var/www/app/plugins/Data/src/Model/Entity/History.php Wrote /var/www/app/plugins/Data/src/Model/Entity/History.php

Baking test fixture for History...

Creating file /var/www/app/plugins/Data/tests/Fixture/HistoryFixture.php Wrote /var/www/app/plugins/Data/tests/Fixture/HistoryFixture.php Bake is detecting possible fixtures...

Baking test case for Data\Model\Table\HistoryTable ...

Creating file /var/www/app/plugins/Data/tests/TestCase/Model/Table/HistoryTableTest.php Wrote /var/www/app/plugins/Data/tests/TestCase/Model/Table/HistoryTableTest.php Done

  1. Loaded the newly created plugin via $this->addPlugin('Data');

  2. Verified the plugin was loaded via bin/cake plugin loaded

  3. Confirmed plugins/Data/src/Model/Table/HistoryTable.php exists

    root@debian:/var/www/app# cat plugins/Data/src/Model/Table/HistoryTable.php
    <?php
    declare(strict_types=1);
    
    namespace Data\Model\Table;
    
    use Cake\ORM\Query;
    use Cake\ORM\RulesChecker;
    use Cake\ORM\Table;
    use Cake\Validation\Validator;
    
    /**
     * History Model
     *
     * @method \Data\Model\Entity\History newEmptyEntity()
     * @method \Data\Model\Entity\History newEntity(array $data, array $options = [])
     * @method \Data\Model\Entity\History[] newEntities(array $data, array $options = [])
     * @method \Data\Model\Entity\History get($primaryKey, $options = [])
     * @method \Data\Model\Entity\History findOrCreate($search, ?callable $callback = null, $options = [])
     * @method \Data\Model\Entity\History patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = [])
     * @method \Data\Model\Entity\History[] patchEntities(iterable $entities, array $data, array $options = [])
     * @method \Data\Model\Entity\History|false save(\Cake\Datasource\EntityInterface $entity, $options = [])
     * @method \Data\Model\Entity\History saveOrFail(\Cake\Datasource\EntityInterface $entity, $options = [])
     * @method \Data\Model\Entity\History[]|\Cake\Datasource\ResultSetInterface|false saveMany(iterable $entities, $options = [])
     * @method \Data\Model\Entity\History[]|\Cake\Datasource\ResultSetInterface saveManyOrFail(iterable $entities, $options = [])
     * @method \Data\Model\Entity\History[]|\Cake\Datasource\ResultSetInterface|false deleteMany(iterable $entities, $options = [])
     * @method \Data\Model\Entity\History[]|\Cake\Datasource\ResultSetInterface deleteManyOrFail(iterable $entities, $options = [])
     *
     * @mixin \Cake\ORM\Behavior\TimestampBehavior
     */
    class HistoryTable extends Table
    {
        /**
         * Initialize method
         *
         * @param array $config The configuration for the Table.
         * @return void
         */
        public function initialize(array $config): void
        {
            parent::initialize($config);
    
            $this->setTable('elements_history');
            $this->setDisplayField('id');
            $this->setPrimaryKey('id');
    
            $this->addBehavior('Timestamp');
    
        }
    
        /**
         * Returns the database connection name to use by default.
         *
         * @return string
         */
        public static function defaultConnectionName(): string
        {
            return 'data';
        }
    }
    
  4. Added the plugin model to an arbitrary controller method:

    • either $this->loadModel('Data.History');,
    • or TableRegistry::getTableLocator()->get('Data.History');
  5. Got the error:

Table class for alias Data.History could not be found. Cake\ORM\Exception\MissingTableClassException CORE/src/ORM/Locator/TableLocator.php:245

What have I tried:

  • I have looked into CORE/src/ORM/Locator/TableLocator.php and I can log that _getClassName receives Data.History, but returns a null. I have also logged $this->locations which it loops through, and the plugin path is not there:

    Array
    (
        [0] => Model/Table
    )
    
  • Added debug to CORE/src/Core/App.php right before it runs a test for \Model\Table\HistoryTable against _classExistsInBase.

    debug([
        '$plugin' => $plugin,
        '$name' => $name,
        '$base' => $base,
        '$fullname' => $fullname,
        '_classExistsInBase' => static::_classExistsInBase($fullname, $base),
    ]);
    
    CORE/src/Core/App.php (line 70)
    [
    '$plugin' => 'Data',
    '$name' => 'History',
    '$base' => 'Data',
    '$fullname' => '\Model\Table\HistoryTable',
    '_classExistsInBase' => false,
    ]
    

Do you know what is the issue and how to fix? I'm on CakePHP 4.2.4 and Bake 2.

ᴍᴇʜᴏᴠ
  • 4,804
  • 4
  • 44
  • 57
  • 2
    Please do not use made up names when it comes to such problems, no matter how sure you are that you didn't made a mistake, there could still be one, and people here would just waste their time. – ndm Mar 19 '21 at 20:49
  • are you using a connection other than the default, for that plugin? – Salines Mar 19 '21 at 23:57
  • @ndm Ok, no problem, I have edited the question with real data I copied from the console – ᴍᴇʜᴏᴠ Mar 20 '21 at 04:57
  • @Salines yes, it uses a different datasource, I have pasted the `HistoryTable` source, it has a `defaultConnectionName` method – ᴍᴇʜᴏᴠ Mar 20 '21 at 04:58
  • 1
    Is the plugin's namespace properly mapped in your `composer.json`, and the autoloader dumped accordingly? – ndm Mar 20 '21 at 09:13
  • @ndm That was it, thank you. Carefully following [plugins.html#manually-autoloading-plugin-classes](https://book.cakephp.org/3/en/plugins.html#manually-autoloading-plugin-classes) has helped. Can you please post this as an answer, so that I can accept and upvote? – ᴍᴇʜᴏᴠ Mar 20 '21 at 16:23
  • Note to my future self coming back here - remember `routes`: `$this->addPlugin('Data', ['routes' => false]);` – ᴍᴇʜᴏᴠ Aug 18 '22 at 12:43

1 Answers1

1

In case of manually created plugins you need to make sure that you update your composer.json's autoloader configuration accordingly and dump the autoloader, otherwise your plugin classes cannot be found.

For your local Data plugin that would look something like this:

{
    // ...
    "autoload": {
        "psr-4": {
            // ...
            "Data\\": "plugins/Data/src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            // ...
            "Data\\Test\\": "plugins/Data/tests/"
        }
    }
}

and then you dump the autoloader:

composer dumpautoload

If you create your plugins using bake, it will ask you whether it should modify your composer.json, and if you allow it it will update it accordingly and dump the autoloader.

See also

ndm
  • 59,784
  • 9
  • 71
  • 110