4spacesdk/CI4OrmExtension

Serialization of Closure is not allowed

Closed this issue · 14 comments

Schop commented

After installing the extension succesfully, I get the following error when trying to get an enityt called Match, which has a Venue

Exception

Serialization of 'Closure' is not allowed

SYSTEMPATH\Cache\Handlers\FileHandler.php at line 134

127 
128         $contents = [
129             'time' => time(),
130             'ttl'  => $ttl,
131             'data' => $value,
132         ];
133 
134         if ($this->writeFile($this->path . $key, serialize($contents)))
135         {
136             chmod($this->path . $key, 0640);
137 
138             return true;
139         }
140 
141         return false;

This is my controller code:

public function __construct() 
    {
        $this->restrictToGroups('admins', base_url('login') );
        $this->matchModel = model('Models\MatchModel');
    }

    public function index()
    {
        
        $this->data['matches'] = $this->matchModel->includeRelated(VenueModel::class)->findAll();

        echo view('admin/match', $this->data);
    }

This is the MatchModel:

<?php namespace App\Models;
use OrmExtension\Extensions\Model;

class MatchModel extends Model
{

    public $hasOne = [
        VenueModel::class,
    ];
}

And this is the VenueModel:

<?php namespace App\Models;
use OrmExtension\Extensions\Model;

class VenueModel extends Model
{
    public $hasMany = [
        MatchModel::class
    ];
}

Can you provide a full stack trace?
Can you try this

$matchModel = new MatchModel();
$this->data['matches'] = $matchModel->includeRelated(VenueModel::class)->find();
Schop commented

I tried it like you suggested, this is the backtrace:

SYSTEMPATH\Cache\Handlers\FileHandler.php : 134   —   serialize()

VENDORPATH\4spacesdk\ci4ormextension\DataMapper\ModelDefinitionCache.php : 128   —  CodeIgniter\Cache\Handlers\FileHandler->save ( arguments )

121 
122 
123 
124 
125     private static function setData($name, $data, $ttl = YEAR) {
126         $instance = ModelDefinitionCache::getInstance();
127         if(isset($instance->cache))
128             $instance->cache->save($name, $data, $ttl);
129     }
130 
131     private $memcache = [];
132     private static function getData($name) {
133         try {
134             $instance = ModelDefinitionCache::getInstance();
135             if(!isset($instance->memcache[$name])) {

VENDORPATH\4spacesdk\ci4ormextension\DataMapper\ModelDefinitionCache.php : 91   —  OrmExtension\DataMapper\ModelDefinitionCache::setData ( arguments )

84                     throw $e;
85             }
86         }
87         return $fieldData;
88     }
89 
90     public static function setRelations($entity, $relations) {
91         static::setData($entity.'_relations', $relations);
92     }
93 
94     /**
95      * @param $entity
96      * @return RelationDef[]
97      * @throws \Exception
98      */

VENDORPATH\4spacesdk\ci4ormextension\DataMapper\ModelDefinitionCache.php : 116   —  OrmExtension\DataMapper\ModelDefinitionCache::setRelations ( arguments )

109                 }
110             }
111             $relations = [];
112             foreach($model->hasOne as $name => $hasOne)
113                 $relations[] = new RelationDef($model, $name, $hasOne, RelationDef::HasOne);
114             foreach($model->hasMany as $name => $hasMany)
115                 $relations[] = new RelationDef($model, $name, $hasMany, RelationDef::HasMany);
116             ModelDefinitionCache::setRelations($entity, $relations);
117         }
118         return $relations;
119     }
120 
121 
122 
123 

VENDORPATH\4spacesdk\ci4ormextension\DataMapper\QueryBuilder.php : 428   —  OrmExtension\DataMapper\ModelDefinitionCache::getRelations ( arguments )

421     }
422 
423     /**
424      * @return RelationDef[]
425      */
426     public function getRelations() {
427         $entityName = $this->_getModel()->getEntityName();
428         $relations = ModelDefinitionCache::getRelations($entityName);
429         return $relations;
430     }
431 
432     // </editor-fold>
433 
434 }
435 

VENDORPATH\4spacesdk\ci4ormextension\DataMapper\QueryBuilder.php : 412   —  OrmExtension\Extensions\Model->getRelations ()

405                 $relation = $relations[0];
406                 $last = $relation->getRelationClass();
407                 $result[] = $relation;
408             }
409             return $result;
410         }
411 
412         foreach($this->getRelations() as $relation) {
413             if($useSimpleName) {
414                 if($relation->getSimpleName() == $name) return [$relation];
415             } else {
416                 if($relation->getName() == $name) return [$relation];
417             }
418         }
419 

VENDORPATH\4spacesdk\ci4ormextension\DataMapper\QueryBuilder.php : 19   —  OrmExtension\Extensions\Model->getRelation ( arguments )

12     /**
13      * @param string|array $relationName
14      * @param null $fields
15      * @return Model
16      */
17     public function includeRelated($relationName, $fields = null): Model {
18         $parent = $this->_getModel();
19         $relations = $this->getRelation($relationName);
20 
21         // Handle deep relations
22         $last = $this;
23         $table = null;
24         $prefix = ''; $relationPrefix = '';
25         foreach($relations as $relation) {
26             $table = $last->addRelatedTable($relation, $prefix, $table, $fields);

APPPATH\Controllers\Admin\Match.php : 22   —  OrmExtension\Extensions\Model->includeRelated ( arguments )

15 
16     public function index()
17     {
18         
19 
20 
21 $matchModel = new MatchModel();
22 $this->data['matches'] = $matchModel->includeRelated(VenueModel::class)->find();
23 
24         echo view('admin/match', $this->data);
25     }
26 
27 
28 }
29 

SYSTEMPATH\CodeIgniter.php : 914   —  App\Controllers\Admin\Match->index ()

907 
908         if (method_exists($class, '_remap'))
909         {
910             $output = $class->_remap($this->method, ...$params);
911         }
912         else
913         {
914             $output = $class->{$this->method}(...$params);
915         }
916 
917         $this->benchmark->stop('controller');
918 
919         return $output;
920     }
921 

SYSTEMPATH\CodeIgniter.php : 400   —  CodeIgniter\CodeIgniter->runController ( arguments )

393         if (! is_callable($this->controller))
394         {
395             $controller = $this->createController();
396 
397             // Is there a "post_controller_constructor" event?
398             Events::trigger('post_controller_constructor');
399 
400             $returned = $this->runController($controller);
401         }
402         else
403         {
404             $this->benchmark->stop('controller_constructor');
405             $this->benchmark->stop('controller');
406         }
407 

SYSTEMPATH\CodeIgniter.php : 308   —  CodeIgniter\CodeIgniter->handleRequest ( arguments )

301 
302             $this->response->pretend($this->useSafeOutput)->send();
303             $this->callExit(EXIT_SUCCESS);
304         }
305 
306         try
307         {
308             return $this->handleRequest($routes, $cacheConfig, $returnResponse);
309         }
310         catch (RedirectException $e)
311         {
312             $logger = Services::logger();
313             $logger->info('REDIRECTED ROUTE at ' . $e->getMessage());
314 
315             // If the route is a 'redirect' route, it throws

FCPATH\index.php : 45   —  CodeIgniter\CodeIgniter->run ()

38 /*
39  *---------------------------------------------------------------
40  * LAUNCH THE APPLICATION
41  *---------------------------------------------------------------
42  * Now that everything is setup, it's time to actually fire
43  * up the engines and make this app do its thang.
44  */
45 $app->run();
46 

It's complaining about a Closure in the relation data. Do you have a Closure in one of your hasOne or hasMany definitions?

Schop commented

I'm not sure if I understand what a closure is, but I don;t think so. The definitions in the models look like this:

MatchModel

<?php namespace App\Models;
use OrmExtension\Extensions\Model;

class MatchModel extends Model
{

    public $hasOne = [
        VenueModel::class,
    ];

}

VenueModel:

<?php namespace App\Models;

use OrmExtension\Extensions\Model;

class VenueModel extends Model
{
    public $hasMany = [
        MatchModel::class
    ];
}

And the entitities are defined like this, without any further code:

<?php namespace App\Entities;
use OrmExtension\Extensions\Entity;

class Venue extends Entity
{
    //
}
``

I've setup a project from scratch with this composer file

"require": {
      "codeigniter4/framework": "4.0.2",
      "4spacesdk/ci4ormextension": "1.0.0-beta.22"
    }

And this database:

CREATE TABLE `matches` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `venue_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

CREATE TABLE `matches` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `venue_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

This controller code

class Home extends BaseController
{
	public function index()
	{
		$matchModel = new MatchModel();
		/** @var Match $matches */
		$matches = $matchModel->includeRelated(VenueModel::class)->find();
		echo $matches->count();
	}

}

No issues found.

Here is a zip of the complete project.
CI4OrmExtensionIssue3.zip

Schop commented

Yes, your test project works for me as well, but my own doesn't. The only difference I can see is that I also have the myth-auth package installed, which should not be an issue I think.

I did find another issue in your test project, but I will open a separate issue for it.

If you provide a copy of your project, I will take a look :)

Schop commented

If you provide a copy of your project, I will take a look :)

It's quite a few mb's, how do you want me to send it to you?

Schop commented

One observation: I do not see the Match_relations file in the writable/cache folder in my project.

You can attach a zip file in a comment here or send it to my email address.

Schop commented

I really appreciate your help. It worked at first, but when I started adding my controllers & views and such, it stopped working. Here is a zip, you just need to adjust the .env file I think.
CI4OrmExtensionIssue3.zip

Also, here is an export of the two tables involved:

euro2021.zip

Try updating to CI4OrmExtension to 1.0.0-beta.23.
The issue was triggered by myth/auth using Closures in a Config file. The config file were attached to model which OrmExtension tried to serialize.
I've adjusted OrmExtension to avoid caching objects and instead use the class name. :)

Schop commented

Thanks for the support @Martin-4Spaces, it works like a charm now.

NP! :)