What causes "Users::load() is not a valid static method." in xPDO?

I’m starting on xPDO and I got a message error in my MODx Error Log, this error says:

(ERROR @ /Applications/MAMP/htdocs/website/core/xpdo/xpdo.class.php : 644) Could not load class: Users from mysql.users.
[2019-09-18 18:02:53] (ERROR @ /Applications/MAMP/htdocs/website/core/xpdo/xpdo.class.php : 762) Users::load() is not a valid static method.

I want to know what cause this error and how to solve it.

I built a database on MySQL and a PHP Snippet:

<?php
$dsn = 'mysql:host=localhost;dbname=prueba_xpdo;port=8888;charset=utf8';
$xpdo = new xPDO($dsn,'user','pass');

echo $o=($xpdo->connect()) ? 'Connected' : 'Not Connected';

$base_path = MODX_CORE_PATH . 'components/one_to_one/';
$modx->addPackage('one_to_one',$base_path.'model/','');
$user = $modx->getObject('Users', array('user_id' => 1 ) );
$userdata = $user->getOne('Userdata');
$output = '';
$output .= $user->get('username');
$output .= $userdata->get('age');
return $output;
?>

This is caused by my PHP Snippet or something who I configured wrong?

My enviroment works in MODx 2.7.1-pl, Apache 2.2.34, PHP 7.2.10 and MySQL 5.7.23

1 Like

Is this a custom db table?
If you’re trying to get the core users table, the object name would be modUser.

The error suggests that addPackage() is failing. Possibly because of its third argument (prefix). It defaults to null, so if there’s no prefix, you should not send that argument. Sending a string makes xPDO try to use it as a prefix because xPDO uses is_string() to test for the existence of a real prefix.

Also, I’m not sure if this is part of your problem, but it might be. Since the modX class extends the xPDO class, you can access any xPDO method using $modx->methodName().

And MODX, once instantiated, already has access to the database, so there’s no need to instantiate xPDO or connect it to the DB. By doing so, you have two instances of xPDO being used in the same code and two connections to the same DB (unless the Users object is in a different database, in which case you wouldn’t be able to access it with $modx->getObject()).

As Digital Penguin says, if you mean to be accessing the standard user object, it should be

$modx->getObject('modUser', array('id' => 1 ) );

I suspect, though, that Users is a custom object, in which case we’d have to know more about how it, and the UserData object are defined if the suggestions above don’t solve the problem.

Is this a custom db table?
If you’re trying to get the core users table, the object name would be modUser.

Yes, I’m using 2 custom database tables, they are called “users” and “userdata” and both exist inside “prueba_xpdo” database.

user table structure:

`user_id` int(11) NOT NULL AUTO_INCREMENT,

`username` varchar(255) DEFAULT NULL,

`bio` text,

`joindate date DEFAULT NULL,

PRIMARY KEY (`user_id`)

and userdata table structure:

`userdata_id` int(11) NOT NULL AUTO_INCREMENT,

`age` tinyint(3) unsigned DEFAULT NULL,

PRIMARY KEY (`userdata_id`)

I’m not extending from “$modx” actually tried before to call a modResource and does Snippet successfully.

<?php
$dsn = 'mysql:host=localhost;dbname=prueba_xpdo;port=8888;charset=utf8';
$xpdo = new xPDO($dsn,'user','pass');

echo $o=($xpdo->connect()) ? 'Connected ' : 'Not Connected ';

$page = $modx->getObject('modResource', 1);
echo $output = $page->get('pagetitle');
?>

with that code, I got the next result:
46%20PM

Notice I’m using a Wayfinder extra at the top to make a list with all existing resources in my website.

I’m trying to get a custom object from a custom database, I explain this in the previous answer to @digitalpenguin

There is a lot of confusion here and a lot we cannot tell from the information you are providing. The first issue is that you are creating a connection to your custom database using a new xPDO object, but then trying to query it using the MODX object (which is connected to the MODX database). That will never work. You are going to need to provide some basic information about what you are trying to accomplish, what your custom xPDO schema looks like for your one_on_one package, etc.

I’m learing to use xPDO, following the example from the xPDO 2.x Documentation, my objective is to get one user from my “prueba_xpdo” database in MySQL.

I’m using a XML schema allocated in core/components/one_to_one/model/schema/one_to_one.mysql.schema.xml and it looks like this:

<object class="Users" table="users" extends="xPDOObject">
        <field key="user_id" dbtype="int" precision="11" phptype="integer" null="false" index="pk"  generated="native" />
        <field key="username" dbtype="varchar" precision="255" phptype="string" null="true" />
        <field key="bio" dbtype="text" phptype="string" null="true" />
        <field key="joindate" dbtype="date" phptype="date" null="true" />
        <index alias="PRIMARY" name="PRIMARY" primary="true" unique="true">
                <column key="user_id" collation="A" null="false" />
        </index>
        <composite alias="Userdata" class="Userdata" local="user_id" foreign="userdata_id" cardinality="one" owner="local" />
</object>
<object class="Userdata" table="userdata" extends="xPDOObject">
        <field key="userdata_id" dbtype="int" precision="11" phptype="integer" null="false" index="pk"  generated="native" />
        <field key="age" dbtype="tinyint" precision="3" attributes="unsigned" phptype="integer" null="true" />
        <index alias="PRIMARY" name="PRIMARY" primary="true" unique="true">
                <column key="userdata_id" collation="A" null="false" />
        </index>
        <aggregate alias="Users" class="Users" local="userdata_id" foreign="user_id" cardinality="one" owner="foreign" />
</object>

I’m writing the PHP code in a snippet with the name “prueba_xPDO” and testing it on my “Inicio” Resource at [[prueba_XPDO]]

Ok, with that, then the problem is simply you are connecting to the database with a separate xPDO instance, but then trying to add the package and query this separate database with the MODX instance. As I said, that will never work. You need to add the package and query the objects from your separate xPDO instance.

It’s not clear to me whether you are trying to avoid using MODX at all. If you’re not (and your use of $modx suggests that you’re not), you might be happier putting your custom tables inside the MODX database for the site and setting them up with something like ClassExtender. Then you could use $modx-> for all your calls. That would also make it possible to connect your custom objects to actual MODX users of the site.

If you are trying to use xPDO without MODX, then ignore this post.

I’ll have a crack at it. It’ll be untested but should at least point you in the right direction.

Firstly, your schema seems to be confusing primary keys with foreign keys. Also to follow convention you’d normally keep the class names singular so User rather than Users. I would also recommend extending xPDOSimpleObject so it takes care of generating and indexing the primary keys automatically for you.
Finally, aim to keep the precision of any varchar fields to maximum 191 so that it can work with UTF8mb4 encoding.

<?xml version="1.0" encoding="UTF-8"?>
<model package="one_to_one" baseClass="xPDOObject" platform="mysql" defaultEngine="InnoDB" phpdoc-package="one_to_one" version="0.1">
    <object class="User" table="users" extends="xPDOSimpleObject">
        <field key="username" dbtype="varchar" precision="191" phptype="string" null="true" />
        <field key="bio" dbtype="text" phptype="string" null="true" />
        <field key="joindate" dbtype="date" phptype="date" null="true" />
        <composite alias="Userdata" class="Userdata" local="id" foreign="user_id" cardinality="one" owner="local" />
    </object>
    <object class="Userdata" table="userdata" extends="xPDOSimpleObject">
        <field key="user_id" dbtype="int" precision="11" phptype="integer" null="false" />
        <field key="age" dbtype="tinyint" precision="3" attributes="unsigned" phptype="integer" null="true" />
        <aggregate alias="User" class="User" local="user_id" foreign="id" cardinality="one" owner="foreign" />
    </object>
</model>

Now that the schema is modified, here’s an updated version of your snippet:

<?php
$modx->addPackage('one_to_one', MODX_CORE_PATH . 'components/one_to_one/model/');
$user = $modx->getObject('User', ['id' => 1]);
$userData = $user->getOne('Userdata');
$output = '';
$output .= $user->get('username');
$output .= $userData->get('age');
return $output;

Or use a query with a join

<?php
$modx->addPackage('one_to_one', MODX_CORE_PATH . 'components/one_to_one/model/');

$c = $modx->newQuery('User');
$c->innerJoin('Userdata','Userdata','Userdata.user_id = User.id');
$c->where([
    'Userdata.age'    =>  20
]);
$c->select('User.id,User.username,Userdata.age');
$users = $modx->getCollection('User',$c);

$output = '';
foreach($users as $user) {
    $output .= $user->get('username').',';
    $output .= $user->get('age').'<br>';
}
return $output;

Hi @digitalpenguin, I tested your code and schema and when I use the query with a join, the site works properly, but the output don’t show anything and the error log update and throw the following result:

[2019-09-24 17:48:10] (ERROR @ /Applications/MAMP/htdocs/anvenwebsite/core/xpdo/xpdo.class.php : 644) Could not load class: User from mysql.user.
[2019-09-24 17:48:10] (ERROR @ /Applications/MAMP/htdocs/website/core/xpdo/xpdo.class.php : 644) Could not load class: Userdata from mysql.userdata.
[2019-09-24 17:48:10] (ERROR @ /Applications/MAMP/htdocs/website/core/xpdo/xpdo.class.php : 592) No class specified for loadClass
[2019-09-24 17:48:10] (ERROR @ /Applications/MAMP/htdocs/website/core/xpdo/xpdo.class.php : 1617) Could not load class 
[2019-09-24 17:48:10] (ERROR @ /Applications/MAMP/htdocs/website/core/xpdo/xpdo.class.php : 592) No class specified for loadClass
[2019-09-24 17:48:10] (ERROR @ /Applications/MAMP/htdocs/website/core/xpdo/xpdo.class.php : 1655) Could not load class !
[2019-09-24 17:48:10] (ERROR @ /Applications/MAMP/htdocs/website/core/xpdo/xpdo.class.php : 592) No class specified for loadClass
[2019-09-24 17:48:10] (ERROR @ /Applications/MAMP/htdocs/website/core/xpdo/xpdo.class.php : 592) No class specified for loadClass
[2019-09-24 17:48:10] (ERROR @ /Applications/MAMP/htdocs/website/core/xpdo/xpdo.class.php : 644) Could not load class: User from mysql.user.
[2019-09-24 17:48:10] (ERROR @ /Applications/MAMP/htdocs/website/core/xpdo/xpdo.class.php : 762) User::loadCollection() is not a valid static method.
[2019-09-24 17:48:10] (ERROR @ /Applications/MAMP/htdocs/website/core/cache/includes/elements/modsnippet/24.include.cache.php : 17) PHP warning: Invalid argument supplied for foreach()

Hi, did you parse the new schema and have all the new class files in the model/one_to_one directory?

To expand on digitalpenguin’s answer: Any time you make changes to your schema, you need to regenerate the class and map files. MODX pays no attention to the schema itself.

You might find some useful information and code here.

1 Like