Creating an xPDO custom model "Could not load class"

I am trying to create a custom model to interact with a custom database table using xPDO. To do this I have been following Bob Ray’s guide Bob's Guides | Custom DB Tables.

I have created the table, and using the snippet provided by Bob to generate the package and the schema and class files which all appears to work fine. The generated directory and file structure of the looks like this below:

image

When I run addPackage on the new quotes package it works fine, however, whenever I run any kind of xPDO function to try and get the data from the custom database table I encounter the errors below.

[2023-02-07 12:30:36] (ERROR @ /home/wetrak/public_html/core/vendor/xpdo/xpdo/src/xPDO/xPDO.php : 666) Could not load class: Quotation from mysql.quotation
[2023-02-07 12:30:36] (ERROR @ /home/wetrak/public_html/core/vendor/xpdo/xpdo/src/xPDO/xPDO.php : 786) Quotation::loadCollection() is not a valid static method.
[2023-02-07 12:30:36] (ERROR @ /home/wetrak/public_html/core/cache/includes/elements/modx/revolution/modsnippet/98.include.cache.php : 14) PHP warning: count(): Parameter must be an array or an object that implements Countable
[2023-02-07 12:30:36] (ERROR @ /home/wetrak/public_html/core/cache/includes/elements/modx/revolution/modsnippet/98.include.cache.php : 16) PHP warning: Invalid argument supplied for foreach()

I have tried creating a custom model using MIGX, ClassExtender and ExtraBuilder but always end up with the same error messages.

I am on MODX 3.0.0-pl - PHP 7.2 - MySQL 5.7.41

The contents of the /core/components/quotes/model/quotes/Quotation.php:

[+class-header+]
[+phpdoc-start+]
[+phpdoc-properties+]
[+phpdoc-end+]
[+class-declaration+]
[+class-traits+][+class-constants+][+class-properties+][+class-methods+][+class-close-declaration+][+class-footer+]

The contents of the /core/components/quotes/model/quotes/metadata.mysql.php:

<?php
$xpdo_meta_map = array (
    'version' => '3.0',
    'namespace' => 'quotes',
    'namespacePrefix' => '',
    'class_map' =>
    array (
        'xPDO\\Om\\xPDOSimpleObject' =>
        array (
            0 => 'quotes\\Quotation',
        ),
    ),
);

The contents of the /core/components/quotes/model/quotes/mysql/Quotation.php:

[+class-header+]
[+class-declaration+]
[+class-traits+][+class-constants+]
    public static $metaMap = array (
        'package' => 'quotes',
        'version' => '3.0',
        'table' => 'quotation',
        'extends' => 'xPDO\\Om\\xPDOSimpleObject',
        'tableMeta' =>
        array (
            'engine' => 'MyISAM',
        ),
        'fields' =>
        array (
            'quote' => NULL,
            'author' => '',
            'topic' => '',
        ),
        'fieldMeta' =>
        array (
            'quote' =>
            array (
                'dbtype' => 'mediumtext',
                'phptype' => 'string',
                'null' => false,
                'index' => 'index',
            ),
            'author' =>
            array (
                'dbtype' => 'varchar',
                'precision' => '200',
                'phptype' => 'string',
                'null' => false,
                'default' => '',
                'index' => 'index',
            ),
            'topic' =>
            array (
                'dbtype' => 'varchar',
                'precision' => '20',
                'phptype' => 'string',
                'null' => false,
                'default' => '',
                'index' => 'index',
            ),
        ),
        'indexes' =>
        array (
            'topic' =>
            array (
                'alias' => 'topic',
                'primary' => false,
                'unique' => false,
                'type' => 'BTREE',
                'columns' =>
                array (
                    'topic' =>
                    array (
                        'length' => '',
                        'collation' => 'A',
                        'null' => false,
                    ),
                ),
            ),
            'author' =>
            array (
                'alias' => 'author',
                'primary' => false,
                'unique' => false,
                'type' => 'BTREE',
                'columns' =>
                array (
                    'author' =>
                    array (
                        'length' => '',
                        'collation' => 'A',
                        'null' => false,
                    ),
                ),
            ),
            'quote' =>
            array (
                'alias' => 'quote',
                'primary' => false,
                'unique' => false,
                'type' => 'FULLTEXT',
                'columns' =>
                array (
                    'quote' =>
                    array (
                        'length' => '',
                        'collation' => '',
                        'null' => false,
                    ),
                    'author' =>
                    array (
                        'length' => '',
                        'collation' => '',
                        'null' => false,
                    ),
                    'topic' =>
                    array (
                        'length' => '',
                        'collation' => '',
                        'null' => false,
                    ),
                ),
            ),
        ),
    );

[+class-properties+][+class-methods+][+class-close-declaration+][+class-footer+]

The contents of the /core/components/quotes/schema/quotes.mysql.schema.xml:

<?xml version="1.0" encoding="UTF-8"?>
<model package="quotes" baseClass="xPDO\Om\xPDOObject" platform="mysql" defaultEngine="MyISAM" version="3.0">
        <object class="Quotation" table="quotation" extends="xPDO\Om\xPDOSimpleObject">
                <field key="quote" dbtype="mediumtext" phptype="string" null="false" index="index" />
                <field key="author" dbtype="varchar" precision="200" phptype="string" null="false" default="" index="index" />
                <field key="topic" dbtype="varchar" precision="20" phptype="string" null="false" default="" index="index" />

                <index alias="topic" name="topic" primary="false" unique="false" type="BTREE" >
                        <column key="topic" length="" collation="A" null="false" />
                </index>
                <index alias="author" name="author" primary="false" unique="false" type="BTREE" >
                        <column key="author" length="" collation="A" null="false" />
                </index>
                <index alias="quote" name="quote" primary="false" unique="false" type="FULLTEXT" >
                        <column key="quote" length="" collation="" null="false" />
                        <column key="author" length="" collation="" null="false" />
                        <column key="topic" length="" collation="" null="false" />
                </index>
        </object>
</model>

Thanks I’ve been struggling with this for a while.

This is definitely wrong.

I’ve seen cases where this happens when you run the parseSchema() function multiple times.
Try deleting the class files manually before running parseSchema() again.

Thanks, I’ve rebuilt the model/quotes/Quotation.php and model/quotes/mysql/Quotation.php. Still getting the same messages in the error log:

[2023-02-07 14:19:34] (ERROR @ /home/wetrak/public_html/core/vendor/xpdo/xpdo/src/xPDO/xPDO.php : 666) Could not load class: Quotation from mysql.quotation
[2023-02-07 14:19:34] (ERROR @ /home/wetrak/public_html/core/vendor/xpdo/xpdo/src/xPDO/xPDO.php : 786) Quotation::loadCollection() is not a valid static method.
[2023-02-07 14:19:34] (ERROR @ /home/wetrak/public_html/core/cache/includes/elements/modx/revolution/modsnippet/98.include.cache.php : 14) PHP warning: count(): Parameter must be an array or an object that implements Countable
[2023-02-07 14:19:34] (ERROR @ /home/wetrak/public_html/core/cache/includes/elements/modx/revolution/modsnippet/98.include.cache.php : 16) PHP warning: Invalid argument supplied for foreach()

Quotation.php:

<?php
namespace quotes;

use xPDO\xPDO;

/**
 * Class Quotation
 *
 * @property string $quote
 * @property string $author
 * @property string $topic
 *
 * @package quotes
 */
class Quotation extends \xPDO\Om\xPDOSimpleObject
{
}

mysql/Quotation.php:

<?php
namespace quotes\mysql;

use xPDO\xPDO;

class Quotation extends \quotes\Quotation
{

    public static $metaMap = array (
        'package' => 'quotes',
        'version' => '3.0',
        'table' => 'quotation',
        'extends' => 'xPDO\\Om\\xPDOSimpleObject',
        'tableMeta' =>
        array (
            'engine' => 'MyISAM',
        ),
        'fields' =>
        array (
            'quote' => NULL,
            'author' => '',
            'topic' => '',
        ),
        'fieldMeta' =>
        array (
            'quote' =>
            array (
                'dbtype' => 'mediumtext',
                'phptype' => 'string',
                'null' => false,
                'index' => 'index',
            ),
            'author' =>
            array (
                'dbtype' => 'varchar',
                'precision' => '200',
                'phptype' => 'string',
                'null' => false,
                'default' => '',
                'index' => 'index',
            ),
            'topic' =>
            array (
                'dbtype' => 'varchar',
                'precision' => '20',
                'phptype' => 'string',
                'null' => false,
                'default' => '',
                'index' => 'index',
            ),
        ),
        'indexes' =>
        array (
            'topic' =>
            array (
                'alias' => 'topic',
                'primary' => false,
                'unique' => false,
                'type' => 'BTREE',
                'columns' =>
                array (
                    'topic' =>
                    array (
                        'length' => '',
                        'collation' => 'A',
                        'null' => false,
                    ),
                ),
            ),
            'author' =>
            array (
                'alias' => 'author',
                'primary' => false,
                'unique' => false,
                'type' => 'BTREE',
                'columns' =>
                array (
                    'author' =>
                    array (
                        'length' => '',
                        'collation' => 'A',
                        'null' => false,
                    ),
                ),
            ),
            'quote' =>
            array (
                'alias' => 'quote',
                'primary' => false,
                'unique' => false,
                'type' => 'FULLTEXT',
                'columns' =>
                array (
                    'quote' =>
                    array (
                        'length' => '',
                        'collation' => '',
                        'null' => false,
                    ),
                    'author' =>
                    array (
                        'length' => '',
                        'collation' => '',
                        'null' => false,
                    ),
                    'topic' =>
                    array (
                        'length' => '',
                        'collation' => '',
                        'null' => false,
                    ),
                ),
            ),
        ),
    );

}

I don’t think there is anything wrong with your schema or class files.

My guess is, that the problem is in your code line that adds the package. Maybe the path is wrong.


I was able to successfully create the class files (and use them in a snippet) by using the extra ExtraBuilder.
The only thing I had to change was to add \Model to the package name (because I think this is what ExtraBuilder expects).

<model package="quotes\Model" ...>

This snippet code seems to work for the class files and file structure you provided:

<?php
$base_path = $modx->getOption('core_path') . 'components/quotes/model/'; 
$modx->addPackage('quotes', $base_path);

use quotes\Quotation;

$items = $modx->getCollection(Quotation::class);

$output = [];
foreach ($items as $item) {
    $item_array = $item->toArray();
    $output[] = json_encode($item_array);
}
return implode("<br>", $output);

creating a package folder and stuff, the MODX 3 way, should also be easy again, starting with this version of MIGX
MIGX/migx-3.0.0-beta1.transport.zip at master · Bruno17/MIGX (github.com)

Are you using ClassExtender, or a different script of mine?

If it’s ClassExtender, there should be an autoload file in the directory that you need to include.

Thanks for the help, it appears to be fixed now.

I’ve tried using ClassExtender, ExtraBuilder and MIGX but always encounter the same error in the end. Looking at the error log it appears that it is looking for a database table with the wrong prefix, modx_ rather than bobs_.

[2023-02-08 10:15:59] (ERROR @ /home/wetrak/public_html/core/vendor/xpdo/xpdo/src/xPDO/Om/xPDOObject.php : 227) Error 42S02 executing statement:
Array
(
[0] => 42S02
[1] => 1146
[2] => Table ‘wetrak.modx_quotation’ doesn’t exist
)

I tried changing the namespacePrefix in core/components/quotes/model/quotes/metadata.mysql.php from an empty string ‘’ to ‘bobs_’ but it gave this error:

[2023-02-08 10:20:24] (ERROR @ /home/wetrak/public_html/core/vendor/xpdo/xpdo/src/xPDO/xPDO.php : 666) Could not load class: quotes\Quotation from mysql.quotes\quotation
[2023-02-08 10:20:24] (ERROR @ /home/wetrak/public_html/core/vendor/xpdo/xpdo/src/xPDO/xPDO.php : 786) quotes\Quotation::loadCollection() is not a valid static method.
[2023-02-08 10:20:24] (ERROR @ /home/wetrak/public_html/core/cache/includes/elements/modx/revolution/modsnippet/98.include.cache.php : 17) PHP warning: Invalid argument supplied for foreach()

So I changed the table prefix from bobs_ to modx_ and it is now working. This goes against Bob’s best practice of

It’s a good practice to use a different table prefix than the one in your MODX database

I wonder if it’s because of not completing this step of Bob Ray’s Guide Bob's Guides | Custom DB Tables so a table_prefix was not specified, though this step is only for connecting to a table in another database.

$xpdo = new xPDO(‘mysql:host=localhost;dbname=mydatabase’ , $database_user,$database_password,$table_prefix);

The namespace prefix is different from the table prefix.

When you use a custom table prefix, you have to specify it in the third parameter when calling addPackage:


I’m not sure if this is really “best practice”. I’d recommend using the default prefix, but then adding a “prefix” to the table name in the schema to avoid naming conflicts with other database tables.

<object class="Quotation" table="myprefix_quotation" ... >

Also maybe use this tutorial instead as it is specifically for MODX 3:

https://docs.modx.com/3.x/en/extending-modx/tutorials/using-custom-database-tables

That advice of mine to use a different prefix is out of date. It was a recommendation (actually a necessity) at one time, but no longer. ClassExtender uses a different prefix by default (ext_) , but you can change it.

For ClassExtender, the bottom line is that the table prefix specified in your schema should match the one specified in the snippet tag on the page with the ClassExtender form.

I suspect that if there’s no table prefix specified in your schema, it will default to modx_. I could be wrong.

I have simillar problem with Bob tutorial. When running CreateXpdoClasses snippet from resource, classes were not created…

[2023-02-16 10:29:56] (ERROR in xPDO\Om\xPDOGenerator::_loadExistingClass @ C:\xampp\htdocs\core\vendor\xpdo\xpdo\src\xPDO\Om\xPDOGenerator.php : 849)
Class "quotes\Quotation" does not exist
[2023-02-16 10:29:56] (ERROR in xPDO\Om\xPDOGenerator::_loadExistingClass @ C:\xampp\htdocs\core\vendor\xpdo\xpdo\src\xPDO\Om\xPDOGenerator.php : 849)
Class "quotes\mysql\Quotation" does not exist

How to fix it?

The ClassExtender extra has been updated for MODX 3, but the CreateXpdoClasses script has not. I’ll get to it eventually, but as of now, I’m not sure what it would take to fix this.

ShowQuotes snippet is also not compatible?

It should be, though I haven’t tested it. More likely, there’s a problem with creating the class file. Can you post your schema?

Also, turn on Dev. Tools (ctrl-shift-i) and watch the Network tab while running the program. It may show 404s for files it can’t find, and you’ll see where it’s looking for them.