4spacesdk / CI4OrmExtension

OrmExtension for CodeIgniter 4
MIT License
50 stars 9 forks source link

Multiple relations #6

Closed Schop closed 4 years ago

Schop commented 4 years ago

I this this is more a question than an issue.

Let's say I have a model called MatchModel, with three relations. I'm assuming you would configure that like so:

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

Then in my controller, to retrieve the Match entity with all related data, I would supposedly do this:

        $matchModel = new MatchModel();

        $this->data['matches'] = $matchModel
                                    ->includeRelated([VenueModel::class, HometeamModel::class, AwayteamModel::class])
                                    ->findAll();

But this results in an exception:


Exception

Failed to find relation App\Models\AwayteamModel for App\Models\VenueModel

C:\xampp\htdocs\euro2021\vendor\4spacesdk\ci4ormextension\DataMapper\QueryBuilder.php at line 420

413             if($useSimpleName) {
414                 if($relation->getSimpleName() == $name) return [$relation];
415             } else {
416                 if($relation->getName() == $name) return [$relation];
417             }
418         }
419 
420         throw new \Exception("Failed to find relation $name for " . get_class($this));
421     }
422 
423     /**
424      * @return RelationDef[]
425      */
426     public function getRelations() {
427         $entityName = $this->_getModel()->getEntityName();

What am I doing wrong?

Martin-4Spaces commented 4 years ago

You would actually do it like this:

 $this->data['matches'] = $matchModel
>includeRelated(VenueModel::class)
->includeRelated(HometeamModel::class)
->includeRelated(AwayteamModel::class)
->findAll();

I know the documentation is sparse. But take a look at https://github.com/4spacesdk/CI4OrmExtension#deep-relations.

includeRelated([VenueModel::class, HometeamModel::class, AwayteamModel::class] means that venue has home team and home team has away team, deep relations :)

Schop commented 4 years ago

I had already tried that, but it gives a MySQL syntax error:

SELECT `matches`.*, `venues`.`id` AS `venues_id`, `venues`.`name` AS `venues_name`, `venues`.`city` AS `venues_city`, `venues`.`country` AS `venues_country`, `venues`.`timediffGMT` AS `venues_timediffGMT`, `venues`.`timezone` AS `venues_timezone`, `venues`.`capacity` AS `venues_capacity`, `venues`.`created_at` AS `venues_created_at`, `venues`.`updated_at` AS `venues_updated_at`, `venues`.`deleted_at` AS `venues_deleted_at`, `matches`., `matches`. FROM `matches` LEFT OUTER JOIN `venues` `venues` ON `venues`.`id` = `matches`.`venue_id` LEFT OUTER JOIN `hometeams` `hometeams` ON `hometeams`.`id` = `matches`.`hometeam_id` LEFT OUTER JOIN `awayteams` `awayteams` ON `awayteams`.`id` = `matches`.`awayteam_id`

It's caused by the " matches., matches." right before the FROM

These are my models:

<?php namespace App\Models;

use OrmExtension\Extensions\Model;

class AwayteamModel extends Model
{
    public $hasOne = [
        MemberModel::class,
    ];

    public $hasMany= [
        MatchModel::class,
      ];

    protected $useTimestamps = true;
    protected $createdField  = 'created_at';
    protected $updatedField  = 'updated_at';
    protected $deletedField  = 'deleted_at';
}
<?php namespace App\Models;

use OrmExtension\Extensions\Model;

class HometeamModel extends Model
{
    public $hasOne = [
        MemberModel::class,
    ];

    public $hasMany= [
        MatchModel::class,
      ];

    protected $useTimestamps = true;
    protected $createdField  = 'created_at';
    protected $updatedField  = 'updated_at';
    protected $deletedField  = 'deleted_at';
}
use OrmExtension\Extensions\Model;

class MatchModel extends Model
{

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

    protected $useTimestamps = true;
    protected $createdField  = 'created_at';
    protected $updatedField  = 'updated_at';
    protected $deletedField  = 'deleted_at';
}
Martin-4Spaces commented 4 years ago

Did you create the db tables and clear writeable/cache? Looks like something is missing. Works fine here. I've extended your project with these tables

CREATE TABLE `awayteams` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `hometeams` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
Schop commented 4 years ago

Okay, I figured out what was missing. Apparently I had never run the find() method on the Hometeam or the Awayteam models, so files like 'hometeam_field_data' did not yet exist in the writeable/cache folder.

After running a findAll() once with all the relations included, it worked.

I think this is a bug though, because I sort of have to create the cache files artifically. If the cache gets cleaned, it will not work again until you run the find() method on all related models at least once.

Martin-4Spaces commented 4 years ago

That would definitely be a bug. But not something I can reproduce.

You could add ModelDefinitionCache::getInstance()->clearCache(); to your BaseController constructor to "disable" the cache in development phase.