Web login via NFC tag

So in the code you should now use

$modx->getObject('Magiclink\\Model\\MagicToken', ...);


use Magiclink\Model\MagicToken;

$modx->getObject(MagicToken::class, ...);

Yes, this has changed as well from MODX 2.x to MODX 3.

   $Token = $modx->getObject('Magiclink\\Model\\MagicToken', [
                    'token' => urldecode($request['token'])

Token is still null…
do I have to modify the path somewhere else ( /src/) except first line?

// Load in our magiclink package 
$path = $modx->getOption('magiclink.core_path', null, $modx->getOption('core_path')) . 'components/magiclink/src/Model/';
$magiclink = $modx->addPackage('magiclink', $path);

I believe this should work:

$path = $modx->getOption('magiclink.core_path', null, $modx->getOption('core_path')) . 'components/magiclink/src/';
$modx->addPackage('Magiclink\Model', $path, null, 'Magiclink\\');

but no change…

Do you still get this “Could not load class” error?

How exactly did you create the table? By using a package like MIGX or ExtraBuilder?
Is there a file core/components/magiclink/src/Model/MagicToken.php with the class MagicToken and namespace Magiclink\Model;?

I got this error:

[2023-03-17 14:53:43] (ERROR @ C:\xampp\htdocs\core\vendor\xpdo\xpdo\src\xPDO\xPDO.php : 666) Could not load class: MagicToken from mysql.magictoken
[2023-03-17 14:53:43] (ERROR @ C:\xampp\htdocs\core\vendor\xpdo\xpdo\src\xPDO\xPDO.php : 786) MagicToken::load() is not a valid static method.
[2023-03-17 15:08:02] (ERROR @ C:\xampp\htdocs\core\vendor\xpdo\xpdo\src\xPDO\Om\xPDOObject.php : 227) Error 42S02 executing statement: 
    [0] => 42S02
    [1] => 1146
    [2] => Table 'modx3.modx_magiclink_tokens' doesn't exist

I tried both migx and extra builder… can I use both?

Both should work. But with MIGX you need at least version 3.0.0-beta1.

After you created the class files with one of these extras, there should be a file bootstrap.php in the folder core/components/magiclink/. This file gets executed whenever MODX is initialized, and it should already add the package correctly with $modx->addPackage(...).

In any snippet/plugin it’s not necessary to call $modx->addPackage() again, and the line $modx->getObject('Magiclink\\Model\\MagicToken', ...); should work without error, because the class is already loaded and known to MODX.

There is still some problem with the class…

[2023-03-20 13:36:22] (ERROR @ C:\xampp\htdocs\core\vendor\xpdo\xpdo\src\xPDO\xPDO.php : 666) Could not load class: MagicToken from mysql.magictoken

I change dthe beginning of the snippet to :

use MODX\Revolution\modUser;
use Magiclink\Model\MagicToken;
// Load in our magiclink package 
$path = $modx->getOption('magiclink.core_path', null, $modx->getOption('core_path')) . 'components/magiclink/src/Model/';
$magiclink = $modx->addPackage('magiclink', $path);

I also had table prefix different then modx_ so I changed it back to modx_, although I didnt find any record about prefix in magiclink folder.
In src/model folder I have:


Like I wrote above, if you have the bootstrap.php file (both MIGX and ExtraBuilder create it), then you don’t need to add the package in the snippet.

That’s not correct. I posted above, how it should be done!

These paths should be src/Model (with an upper case M!)

ok since I dont have to add addPackage, why do I define $path in the first place?
Files are generated by migx so it should be correct (with capital M) - I just didnt write it correctly in my comment.
Bootstrap.php is present:


 * @var \MODX\Revolution\modX $modx
 * @var array $namespace

use Magiclink\Magiclink;
use xPDO\xPDO;

// Add the service
try {
    // Add the package and model classes
    $modx->addPackage('Magiclink\Model', $namespace['path'] . 'src/', null, 'Magiclink\\');

    if (class_exists('Magiclink\\Magiclink')) {
        $modx->services->add('Magiclink', function($c) use ($modx) {
            return new Magiclink($modx);
catch (\Exception $e) {
    $modx->log(xPDO::LOG_LEVEL_ERROR, $e->getMessage());

On line 63:
$MagicToken = $modx->newObject(‘MagicToken’… is null

This is wrong! Like I already wrote above, use

$modx->getObject('Magiclink\\Model\\MagicToken', ...);


use Magiclink\Model\MagicToken;


$modx->getObject(MagicToken::class, ...);

You don’t!

Ok its got a bit confusing now. In my previous post I sent beggining of the snippet, where I have:

use MODX\Revolution\modUser;
use Magiclink\Model\MagicToken;

Therefore I didnt modify:

            $MagicToken = $modx->newObject('MagicToken', [
                'token'     => $token,
                'expires'   => $expires,
                'userid'    => $User->get('id')

Even If dont use addPackage I stil didnt get any further. I also noticed that in bootstrap.php it is:
use Magiclink\Magiclink; is that correct?

Use MagicToken::class instead of the string 'MagicToken':

$MagicToken = $modx->newObject(MagicToken::class, [ ... ]);

Ok, thanks. This works for newObject.
There si getObject on line 101:

$Token = $modx->getObject(MagicToken::class, [
                    'token' => urldecode($request['token'])

It returns null.
in src/Model/metadata.mysql.hp:

$xpdo_meta_map = array (
    'version' => '3.0',
    'namespace' => 'magic\\Model',
    'namespacePrefix' => 'magic',
    'class_map' => 
    array (
        'xPDO\\Om\\xPDOSimpleObject' => 
        array (
            0 => 'magic\\Model\\magic',

is this correct?

And does a row in the database exist, where the value of the column token is equal to the value of the “token” GET request parameter?

Yes it does exists.
I rewrote the whole script like this, and it works:

// Output
$output = "";
// Namespace
$namespace = "magic\\Model\\";
$request = $modx->sanitize($_REQUEST);

     // Check that we have the token set in the request, if not just return 
                if(empty($request['token'])) return;
                // Lets grab the DB entry for this token 
                $Token = $modx->getObject($namespace.'magic', [
                    'token' => urldecode($request['token'])
                if(!is_object($Token)) {
                    throw new Exception("Invalid token", 403);
   $User = $modx->getObject('modUser', $Token->get('userid'));
        $username = $User->get('username');
       // $output .= ' login: ' . $username . '<br/>';
  // All being well we can now log the user in 
     if(!is_object($User)) {
         throw new Exception("Unable to locate your user account, please try again", 404);
   // Log the user in to the web context
 // Forward the user to the dashboard 
 $url = $modx->makeUrl(11);
// Return output

Can you see the difference, the reason why the previous didnt work?

Why is your namespace and the name of your class suddenly different?
How should I (or anyone else) be able to give you advice, if you keep changing things?