Web login via NFC tag

I always got stuck on
$modx->getObject(‘MagicToken’ or
$modx->newObject(‘MagicToken’
It always return null

Did you create this class “MagicToken”? Did you create it in MODX 3?
In MODX 3, all classes need a namespace. So you have to change the schema from the article you linked.

I changed the schema like this:

<?xml version="1.0" encoding="UTF-8"?>

<model package="Magiclink\Model\" baseClass="xPDO\Om\xPDOObject" platform="mysql" defaultEngine="InnoDB" phpdoc-package="" phpdoc-subpackage="" version="3.0">

      <object class="MagicToken" table="magiclink_tokens" extends="xPDOSimpleObject">
        <field key="token" dbtype="varchar" phptype="string" precision="50" null="false" default="" />
        <field key="userid" dbtype="int" phptype="int" precision="20" null="false" default="" />        
    	<field key="expires" dbtype="datetime" phptype="datetime" null="true" />
    </object>

</model>

I also noticed that path to model is magiclink/src/Model , but in php code it is only magiclink/model

So in the code you should now use

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

or

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\\');

thanks,
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: 
Array
(
    [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:

src/model/mysql/MagicToken.php
src/model/MagicToken.php
src/model/metadata.mysql.php

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:

<?php

/**
 * @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', ...);

or

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:

<?php
$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:

<?php
// 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
   $User->addSessionContext('web');       
 
 // Forward the user to the dashboard 
 $url = $modx->makeUrl(11);
 $modx->sendRedirect($url);
                
// Return output
return;

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