定制 efabrica/nette-repository 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

efabrica/nette-repository

Composer 安装命令:

composer require efabrica/nette-repository

包简介

Repositories for Nette database

README 文档

README

This extension enhances the static analysis of your Nette Database by providing typehinted entities (ActiveRows), queries (Selections) and repositories (services).

Installation

You can install this extension using Composer, the dependency manager for PHP:

composer require efabrica/nette-repository

To enable the extension, you need to register it in config:

extensions:
    netteRepo: Efabrica\NetteRepository\Bridge\EfabricaNetteRepositoryExtension

Finally, you can run the repository code generation command to generate the necessary classes and files:

$ php vendor/bin/enc

Usage

Entity

The Entity class is a subclass of ActiveRow that serves as a superclass for your entities. You can either use our code generation tool to create it automatically or write it manually.

Inserting an entity

assert($repository instanceof PersonRepository);
assert($entity instanceof Person);
$entity = $repository->create();
$entity->name = 'John';
$entity->surname = 'Doe';
$entity->age = 42;
$entity->save();

Save array:

$entity = $repository->insertOne([
    Person::NAME => 'John',
    Person::SURNAME => 'Doe',
    Person::AGE => 42,
]); // always returns entity

// More verbose approach:
$entity = $repository->create();
$entity->fill([
    Person::NAME => 'John',
    Person::SURNAME => 'Doe',
    Person::AGE => 42,
]);
$entity->save();

Classic:

$person = $repository->insert([
    Person::NAME => 'John',
    Person::SURNAME => 'Doe',
    Person::AGE => 42,
]); // always returns Entity

Multi-insert:

$persons = [];
foreach (range(30, 40) as $age) {
    $person = $repository->createRow();
    Person::NAME => 'John',
    Person::SURNAME => 'Doe',
    Person::AGE => $age,
    $persons[] = $person;
}
$repository->insertMany($persons); // always returns int

Updating an entity

$entity = $repository->find($id);
$entity->name = 'Jake';
$entity->update(); // or $entity->save();

Classic:

$repository->update($id, [Person::name => 'Jake']);

Deleting an entity

$repository->delete($id);

Or, if you already have the entity available:

$entity = $repository->find($id);
$entity->delete();

Scopes

Scope is a class that defines which existing behaviors are disabled for the Repository, Query and Entity.

The active Scope is passed down from Repository to Query and from Query down to Entity.

->withScope(Scope $scope) returns a clone of the object with the given scope applied.

->scopeRaw() returns a clone of the object with raw scope. Raw scope removes all behaviors.

->scopeFull() returns a clone of the object with full scope. Full scope keeps all behaviors. This is the default scope, unless you change the scope in repository's setup() method.

Example

final class AdminScope implements \Efabrica\NetteRepository\Repository\Scope\Scope
{
    public function apply(RepositoryBehaviors $behaviors, Repository $repository): void
    {
        // Remove these behaviors because they are not needed for the Admin
        $behaviors
            ->remove(SoftDeleteBehavior::class)
            ->remove(PublishBehavior::class);
        
        // Do not add any new behaviors here, because this scope can be used by different repositories
        // and you might introduce unwanted side effects.
        
        // However, you can conditionally add behaviors based on the repository type and some parameter
        // For example, if you want to apply a special behavior for admin users in the user repository, you can do this:
        if ($repository instanceof UserRepository && $someContainerParameter) { // Scopes can be services and receive parameters.
            $behaviors->add(AdminBehavior::class); // This is a hypothetical behavior, just for illustration.
        }
    }
}

To use the Scope as a container service, which may not be necessary in your case, please follow these steps:

use Efabrica\NetteRepository\Repository\RepositoryBehaviors;
abstract class RepositoryBase extends \Efabrica\NetteRepository\Repository\Repository
{
    /** @inject */
    public AdminScope $adminScope;
    
    public function scopeAdmin(): self
    {
        return $this->withScope($this->adminScope);
    }
    
    // Do this if you want to set the AdminScope as default:
    protected function setup(RepositoryBehaviors $behaviors) : void 
    {
        $behaviors->setScope($this->adminScope);
    }
}

And optionally implement shorthand methods for your queries:

use YourBeautifulApplication\Admin\AdminScope;

abstract class QueryBase extends \Efabrica\NetteRepository\Repository\Query
{
    public function scopeAdmin(): self
    {
        // This method returns a copy of the query with your own Admin scope applied
        // The Admin scope removes some behaviors that are not relevant for the Admin
        return $this->withScope($this->repository->adminScope);
        // Alternatively, if the Admin scope does not depend on any parameters, you can create a new instance of it like this:
        return $this->withScope(new AdminScope());
    }
}

Usage:

// all of these are equivalent:
$repository->findBy(['age > ?' => 18])->scopeAdmin()->fetchAll();
$repository->scopeAdmin()->findBy(['age > ?' => 18])->fetchAll();
$repository->query()->where('age > ?', 18)->scopeAdmin()->fetchAll();
$repository->scopeAdmin()->query()->where('age > ?', 18)->fetchAll();
$repository->query()->scopeAdmin()->where('age > ?', 18)->fetchAll();

Code Generator

Code generation is fully optional, but it is recommended to use it.

To run the code generation, use this command:

$ php bin/console efabrica:nette-repo:code-gen
# OR
$ php bin/console e:n:c
# OR 
$ php vendor/bin/enc

For every table in the database, it will generate these classes in the /Generated/ namespace: (Example: person table)

  • Repository\Generated\Repository\PersonRepositoryBase - Repository base class, holds typehints for PersonQuery and Person entity. ( abstract)
  • Repository\Generated\Query\PersonQuery - Query class, holds typehints for Person entity. (abstract)
  • Repository\Generated\Entity\Person - Entity class, holds types for columns and public constants for column names. (final)

These classes are always regenerated when you run the code generator. They should not be modified manually.

For every table in the database, it will also generate these classes outside of the /Generated/ namespace, but only if they don't exist. If they exist, they will not be overwritten:

  • Repository\PersonRepository - extends PersonRepositoryBase. Here you write your custom repository methods. (final)
  • Repository\Query\PersonQuery - extends PersonQuery. Here you write your custom query methods. (final)
  • Repository\Entity\PersonBody - trait that is inserted into Person entity. Here you write your custom entity methods. (trait)

These classes are not regenerated when you run the code generator. They are meant to be customized by you. If you want to regenerate them, you have to delete them first.

Ignoring tables

It is also possible to ignore some tables. To do that, you can modify the ignoredTables parameter in the config file:

netteRepo:
    ignoreTables:
        # These are the defaultly ignored tables:
        migrations: true
        migration_log: true
        phoenix_log: true
        phinxlog: true

Table alias

You can define a custom alias for specific tables using the tableAlias parameter in the config file. The defined alias will be used as the entity name when generating the structure:

netteRepo:
    tableAlias:
        matches: games

Custom Inheritance

If you want to set different extends or implements for a generated class, you can do that by adding an entry into your config file:

netteRepo:
    inheritance:
        AuthorRepositoryBase:
            extends: 'App\Repository\PeopleRepositoryBase'
            implements: ['App\Repository\PersonRepositoryInterface']
        AuthorQuery:
            extends: 'App\Repository\PeopleQueryBase'
        Person:
            implements: ['App\Repository\PersonInterface']
  • Every generated class can be used for this.
  • Key is the short class name (without namespace). Generated classes are made such that there are no namespace collisions, so this shouldn't prove a problem.
  • extends must be string or null/not specified.
  • implements must be string array or empty array or null/not specified.
  • You cannot unimplement an interface.

This config schema is a bit verbose, but very intuitive once you see it and easy to read.

Behaviors and Traits

DateBehavior

This behavior automatically sets the created_at and updated_at columns to the current date and time when inserting or updating a row.

FilterBehavior

This behavior applies a default where() condition to every select query.

KeepDefaultBehavior

This behavior ensures that there is always at least one row with a truthy value in the default column. This is useful for flag columns.

SoftDeleteBehavior

This behavior marks a row as deleted by setting the deleted_at column to the current date and time instead of removing it from the table.

LastManStandingBehavior

This behavior prevents deleting the last row in the table that matches a given query.

TreeTraverseBehavior

This behavior manages the lft and rgt columns that represent the hierarchical structure of the table. It updates them automatically when inserting or updating a row.

SortingTrait

This trait adds methods to the repository for changing the order of rows, such as moveUp(), moveDown(), insertBefore(), and insertAfter().

CastBehavior

This behavior automatically casts values to the specified type when retrieving them from the database.

There are some predefined casts, but you can also define your own.

  • JsonCastBehavior: casts values from JSON to PHP array and vice versa.
  • CarbonCastBehavior: casts values from MySQL datetime to CarbonImmutable and vice versa.

Events

There are several events that you can listen to in your repository: Insert, Update, Delete, Select, Load

To implement your own event subscriber, create a new class that extends Efabrica\NetteRepository\Subscriber\EventSubscriber and register it in the container. It will get automatically detected, since it extends the EventSubscriber.

efabrica/nette-repository 适用场景与选型建议

efabrica/nette-repository 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 53.1k 次下载、GitHub Stars 达 3, 最近一次更新时间为 2023 年 08 月 02 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「database」 「php」 「repository」 「nette」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 efabrica/nette-repository 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 efabrica/nette-repository 我们能提供哪些服务?
定制开发 / 二次开发

基于 efabrica/nette-repository 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 53.1k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 3
  • 点击次数: 9
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 3
  • Watchers: 4
  • Forks: 10
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2023-08-02