cycle / orm

PHP DataMapper, ORM
https://cycle-orm.dev
MIT License
1.23k stars 72 forks source link

Ошибка при сохранении связи ManyToMany между сущностями из одной таблицы #171

Closed hustlahusky closed 3 years ago

hustlahusky commented 3 years ago

Пытаюсь организовать хранение ролевой модели с храниением ролей и прав доступа в одной таблице, как это было сделано в yii2.

Схему БД для Сycle описываю через schema-builder

    private function registerRbacItemEntity(Registry $registry): void
    {
        $rbacItem = new Entity();
        $rbacItem->setRole('rbac_item');

        $rbacItem->getFields()
            ->set(
                'name',
                (new Field())->setType('string(255)')->setColumn('name')->setPrimary(true)
            )
            ->get('name')
            ->getOptions()
            ->set(self::COL_NULLABLE, false);
        $rbacItem->getFields()
            ->set(
                'type',
                (new Field())->setType('string(16)')->setColumn('type')
            )
            ->get('type')
            ->getOptions()
            ->set(self::COL_NULLABLE, false);
        $rbacItem->getFields()
            ->set(
                'description',
                (new Field())->setType('text')->setColumn('description')
            )
            ->get('description')
            ->getOptions()
            ->set(self::COL_NULLABLE, true);
        $rbacItem->getFields()
            ->set(
                'createdAt',
                (new Field())->setType('datetime')->setColumn('created_at')
            )
            ->get('createdAt')
            ->getOptions()
            ->set(self::COL_NULLABLE, true)
            ->set(self::COL_DEFAULT, AbstractColumn::DATETIME_NOW);
        $rbacItem->getFields()
            ->set(
                'updatedAt',
                (new Field())->setType('datetime')->setColumn('updated_at')
            )
            ->get('updatedAt')
            ->getOptions()
            ->set(self::COL_NULLABLE, true)
            ->set(self::COL_DEFAULT, AbstractColumn::DATETIME_NOW);

        $rbacItem->getRelations()
            ->set(
                'parents',
                (new Relation())
                    ->setTarget('rbac_item')
                    ->setType('manyToMany')
                    ->setInverse('children', 'manyToMany')
            )
            ->get('parents')
            ->getOptions()
            ->set('through', 'rbac_item_inheritance')
            ->set('throughOuterKey', 'parent')
            ->set('throughInnerKey', 'child')
            ->set('nullable', true);

        $rbacItem->setMapper(RbacItemMapperCycle::class);

        $this->autocompleteEntity($rbacItem);
        $registry->register($rbacItem);
        $registry->linkTable($rbacItem, null, 'rbac_item');

        $rbacRole = new Entity();
        $rbacRole->setRole('rbac_role');
        $rbacRole->setClass(Role::class);

        $registry->registerChild($rbacItem, $rbacRole);

        $rbacPermission = new Entity();
        $rbacPermission->setRole('rbac_permission');
        $rbacPermission->setClass(Permission::class);

        $registry->registerChild($rbacItem, $rbacPermission);
    }

    private function registerRbacItemInheritanceEntity(Registry $registry): void
    {
        $rbacItemInheritance = new Entity();
        $rbacItemInheritance->setRole('rbac_item_inheritance');

        $rbacItemInheritance->getFields()
            ->set(
                'id',
                (new Field())->setType('uuid')->setColumn('id')->setPrimary(true)
            )
            ->get('id')
            ->getOptions()
            ->set(self::COL_NULLABLE, false);
        $rbacItemInheritance->getFields()
            ->set(
                'parent',
                (new Field())->setType('string(255)')->setColumn('parent')
            )
            ->get('parent')
            ->getOptions()
            ->set(self::COL_NULLABLE, false);
        $rbacItemInheritance->getFields()
            ->set(
                'child',
                (new Field())->setType('string(255)')->setColumn('child')
            )
            ->get('child')
            ->getOptions()
            ->set(self::COL_NULLABLE, false);

        $this->autocompleteEntity($rbacItemInheritance);
        $registry->register($rbacItemInheritance);
        $registry->linkTable($rbacItemInheritance, null, 'rbac_item_inheritance');
    }

При сохранении роли вместе с привязкой к праву доступа возникает ошибка

Message: SplObjectStorage::contains() expects parameter 1 to be object, null given
File: /var/www/html/vendor/cycle/orm/src/Relation/ManyToMany.php
Line: 219

Возникает она из-за того что в \Cycle\ORM\Relation\ManyToMany::sortRelation попадают ноды с одинаковой ролью, из-за чего они схлопываются в одну.

Полный стек трейс ``` Type: TypeError Code: 0 Message: SplObjectStorage::contains() expects parameter 1 to be object, null given File: /var/www/html/vendor/cycle/orm/src/Relation/ManyToMany.php Line: 219 Trace: #0 /var/www/html/vendor/cycle/orm/src/Relation/ManyToMany.php(219): SplObjectStorage->contains(NULL) #1 /var/www/html/vendor/cycle/orm/src/Relation/ManyToMany.php(172): Cycle\ORM\Relation\ManyToMany->initPivot(Object(Cycle\ORM\Heap\Node), Object(App\Domain\Rbac\Role), NULL) #2 /var/www/html/vendor/cycle/orm/src/Relation/ManyToMany.php(141): Cycle\ORM\Relation\ManyToMany->link(Object(Cycle\ORM\Heap\Node), Object(App\Domain\Rbac\Role), NULL, Object(Cycle\ORM\Relation\Pivoted\PivotedStorage)) #3 /var/www/html/vendor/cycle/orm/src/RelationMap.php(183): Cycle\ORM\Relation\ManyToMany->queue(Object(Cycle\ORM\Command\Database\Insert), Object(App\Domain\Rbac\Permission), Object(Cycle\ORM\Heap\Node), Object(Cycle\ORM\Relation\Pivoted\PivotedStorage), Object(Cycle\ORM\Relation\Pivoted\PivotedStorage)) #4 /var/www/html/vendor/cycle/orm/src/RelationMap.php(139): Cycle\ORM\RelationMap->queueRelation(Object(Cycle\ORM\Command\Database\Insert), Object(App\Domain\Rbac\Permission), Object(Cycle\ORM\Heap\Node), Object(Cycle\ORM\Relation\ManyToMany), Object(Cycle\ORM\Relation\Pivoted\PivotedStorage), NULL) #5 /var/www/html/vendor/cycle/orm/src/ORM.php(358): Cycle\ORM\RelationMap->queueRelations(Object(Cycle\ORM\Command\Database\Insert), Object(App\Domain\Rbac\Permission), Object(Cycle\ORM\Heap\Node), Array) #6 /var/www/html/vendor/cycle/orm/src/Relation/ManyToMany.php(166): Cycle\ORM\ORM->queueStore(Object(App\Domain\Rbac\Permission)) #7 /var/www/html/vendor/cycle/orm/src/Relation/ManyToMany.php(141): Cycle\ORM\Relation\ManyToMany->link(Object(Cycle\ORM\Heap\Node), Object(App\Domain\Rbac\Permission), NULL, Object(Cycle\ORM\Relation\Pivoted\PivotedStorage)) #8 /var/www/html/vendor/cycle/orm/src/RelationMap.php(183): Cycle\ORM\Relation\ManyToMany->queue(Object(Cycle\ORM\Command\Database\Insert), Object(App\Domain\Rbac\Role), Object(Cycle\ORM\Heap\Node), Object(Cycle\ORM\Relation\Pivoted\PivotedStorage), Object(Cycle\ORM\Relation\Pivoted\PivotedStorage)) #9 /var/www/html/vendor/cycle/orm/src/RelationMap.php(139): Cycle\ORM\RelationMap->queueRelation(Object(Cycle\ORM\Command\Database\Insert), Object(App\Domain\Rbac\Role), Object(Cycle\ORM\Heap\Node), Object(Cycle\ORM\Relation\ManyToMany), Object(Cycle\ORM\Relation\Pivoted\PivotedStorage), NULL) #10 /var/www/html/vendor/cycle/orm/src/ORM.php(358): Cycle\ORM\RelationMap->queueRelations(Object(Cycle\ORM\Command\Database\Insert), Object(App\Domain\Rbac\Role), Object(Cycle\ORM\Heap\Node), Array) #11 /var/www/html/vendor/cycle/orm/src/Transaction.php(184): Cycle\ORM\ORM->queueStore(Object(App\Domain\Rbac\Role), 0) #12 /var/www/html/vendor/cycle/orm/src/Transaction.php(94): Cycle\ORM\Transaction->initCommands() #13 /var/www/html/vendor/spaceonfire/data-source/src/Bridge/CycleOrm/Repository/AbstractCycleRepositoryAdapter.php(65): Cycle\ORM\Transaction->run() #14 /var/www/html/src/Application/Auth/Role/CreateRoleCommandHandler.php(43): spaceonfire\DataSource\Bridge\CycleOrm\Repository\AbstractCycleRepositoryAdapter->save(Object(App\Domain\Rbac\Role)) #15 /var/www/html/vendor/spaceonfire/command-bus/src/CommandBus.php(81): App\Application\Auth\Role\CreateRoleCommandHandler->__invoke(Object(App\Application\Auth\Role\CreateRoleCommand)) #16 /var/www/html/src/Shared/Infrastructure/CQRS/Middleware/ValidationMiddleware.php(40): spaceonfire\CommandBus\CommandBus->spaceonfire\CommandBus\{closure}(Object(App\Application\Auth\Role\CreateRoleCommand)) #17 /var/www/html/vendor/spaceonfire/command-bus/src/CommandBus.php(87): App\Shared\Infrastructure\CQRS\Middleware\ValidationMiddleware->execute(Object(App\Application\Auth\Role\CreateRoleCommand), Object(Closure)) #18 /var/www/html/vendor/spaceonfire/command-bus/src/Bridge/PsrLog/LoggerMiddleware.php(114): spaceonfire\CommandBus\CommandBus->spaceonfire\CommandBus\{closure}(Object(App\Application\Auth\Role\CreateRoleCommand)) #19 /var/www/html/vendor/spaceonfire/command-bus/src/CommandBus.php(87): spaceonfire\CommandBus\Bridge\PsrLog\LoggerMiddleware->execute(Object(App\Application\Auth\Role\CreateRoleCommand), Object(Closure)) #20 /var/www/html/vendor/spaceonfire/command-bus/src/CommandBus.php(71): spaceonfire\CommandBus\CommandBus->spaceonfire\CommandBus\{closure}(Object(App\Application\Auth\Role\CreateRoleCommand)) #21 /var/www/html/vendor/spaceonfire/common/src/CQRS/Command/AbstractCommandBus.php(26): spaceonfire\CommandBus\CommandBus->handle(Object(App\Application\Auth\Role\CreateRoleCommand)) #22 /var/www/html/src/Infrastructure/Http/Controllers/Auth/Role/CreateRoleController.php(71): spaceonfire\Common\CQRS\Command\AbstractCommandBus->dispatch(Object(App\Application\Auth\Role\CreateRoleCommand)) #23 /var/www/html/vendor/slim/slim/Slim/Handlers/Strategies/RequestResponse.php(43): App\Infrastructure\Http\Controllers\Auth\Role\CreateRoleController->__invoke(Object(App\Shared\Infrastructure\Http\JsonApi\JsonApiRequest), Object(Slim\Psr7\Response), Array) #24 /var/www/html/vendor/slim/slim/Slim/Routing/Route.php(384): Slim\Handlers\Strategies\RequestResponse->__invoke(Array, Object(Slim\Psr7\Request), Object(Slim\Psr7\Response), Array) #25 /var/www/html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(81): Slim\Routing\Route->handle(Object(Slim\Psr7\Request)) #26 /var/www/html/src/Infrastructure/Http/Middleware/Auth/AuthenticateByTokenMiddleware.php(31): Slim\MiddlewareDispatcher->handle(Object(Slim\Psr7\Request)) #27 /var/www/html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(209): App\Infrastructure\Http\Middleware\Auth\AuthenticateByTokenMiddleware->process(Object(Slim\Psr7\Request), Object(Slim\MiddlewareDispatcher)) #28 /var/www/html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(81): class@anonymous->handle(Object(Slim\Psr7\Request)) #29 /var/www/html/vendor/slim/slim/Slim/Routing/Route.php(341): Slim\MiddlewareDispatcher->handle(Object(Slim\Psr7\Request)) #30 /var/www/html/vendor/slim/slim/Slim/Routing/RouteRunner.php(84): Slim\Routing\Route->run(Object(Slim\Psr7\Request)) #31 /var/www/html/vendor/yiisoft/session/src/SessionMiddleware.php(36): Slim\Routing\RouteRunner->handle(Object(Slim\Psr7\Request)) #32 /var/www/html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(209): Yiisoft\Session\SessionMiddleware->process(Object(Slim\Psr7\Request), Object(Slim\Routing\RouteRunner)) #33 /var/www/html/vendor/middlewares/payload/src/Payload.php(75): class@anonymous->handle(Object(Slim\Psr7\Request)) #34 /var/www/html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(209): Middlewares\Payload->process(Object(Slim\Psr7\Request), Object(class@anonymous)) #35 /var/www/html/vendor/middlewares/client-ip/src/ClientIp.php(65): class@anonymous->handle(Object(Slim\Psr7\Request)) #36 /var/www/html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(209): Middlewares\ClientIp->process(Object(Slim\Psr7\Request), Object(class@anonymous)) #37 /var/www/html/vendor/slim/slim/Slim/Middleware/RoutingMiddleware.php(59): class@anonymous->handle(Object(Slim\Psr7\Request)) #38 /var/www/html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(147): Slim\Middleware\RoutingMiddleware->process(Object(Slim\Psr7\Request), Object(class@anonymous)) #39 /var/www/html/vendor/slim/slim/Slim/Middleware/ErrorMiddleware.php(107): class@anonymous->handle(Object(Slim\Psr7\Request)) #40 /var/www/html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(147): Slim\Middleware\ErrorMiddleware->process(Object(Slim\Psr7\Request), Object(class@anonymous)) #41 /var/www/html/vendor/middlewares/cors/src/Cors.php(50): class@anonymous->handle(Object(Slim\Psr7\Request)) #42 /var/www/html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(209): Middlewares\Cors->process(Object(Slim\Psr7\Request), Object(class@anonymous)) #43 /var/www/html/src/Shared/Infrastructure/Http/Middleware/MonologPushProcessorMiddleware.php(43): class@anonymous->handle(Object(Slim\Psr7\Request)) #44 /var/www/html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(147): App\Shared\Infrastructure\Http\Middleware\MonologPushProcessorMiddleware->process(Object(Slim\Psr7\Request), Object(class@anonymous)) #45 /var/www/html/src/Shared/Infrastructure/Http/Middleware/RequestIdMiddleware.php(45): class@anonymous->handle(Object(Slim\Psr7\Request)) #46 /var/www/html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(209): App\Shared\Infrastructure\Http\Middleware\RequestIdMiddleware->process(Object(Slim\Psr7\Request), Object(class@anonymous)) #47 /var/www/html/vendor/slim/slim/Slim/MiddlewareDispatcher.php(81): class@anonymous->handle(Object(Slim\Psr7\Request)) #48 /var/www/html/vendor/slim/slim/Slim/App.php(215): Slim\MiddlewareDispatcher->handle(Object(Slim\Psr7\Request)) #49 /var/www/html/public/index.php(103): Slim\App->handle(Object(Slim\Psr7\Request)) #50 {main} [] {"request_id":"489b73a5-17c4-44f6-8316-68951cfc1d69"} ```
hustlahusky commented 3 years ago

Кажется понял в чем дело. PR постараюсь оформить самостоятельно