impulseit/bitrix-models

v0.12.0 2024-01-25 08:57 UTC

This package is not auto-updated.

Last update: 2024-09-20 10:22:39 UTC


README

介绍

此包引入了 Model Layer 到 Bitrix。该层逻辑上由两部分组成

  1. 用于 Bitrix 实体的模型(以下简称“Bitrix 模型”),在 Bitrix API 内部工作(例如 CIBlockElement 等),并在其之上构建。从外部来看,这个构建类似于 Eloquent
  2. 用于任意表的模型,通过 illuminate/database 以及特别地 Eloquent 工作。

安装

  1. 
    
  2. init.php 中注册包 - Arrilot\BitrixModels\ServiceProvider::register();

使用 Bitrix 模型

以下模型可用于继承

Arrilot\BitrixModels\Models\ElementModel
Arrilot\BitrixModels\Models\SectionModel
Arrilot\BitrixModels\Models\UserModel
Arrilot\BitrixModels\Models\D7Model

为了举例,我们将使用元素信息块(ElementModel)的模型。对于其他实体,API 几乎相同。

ElementModel 仅完全支持第二版本的元素信息块(即存储属性在单独的表中)。第一版本可能由于 CIBlockElement::GetList() 的特性而无法正常工作。最大问题是:如果信息块中存在多个属性,则 limit()、take() 和 first() 的查询将不正确,并且会得到比预期更少的元素和未完整的多属性集。如果您确实打算使用 ElementModel 与第一版本的信息块一起使用,那么请在模型类中设置 const IBLOCK_VERSION = 1;。

创建商品信息块的模型。

<?php

use Arrilot\BitrixModels\Models\ElementModel;

class Product extends ElementModel
{
    /**
     * Corresponding iblock id.
     *
     * @return int
     */
    const IBLOCK_ID = 1;
}

为了使用模型,只需在常量中指定信息块的 ID。对于用户来说,这也不需要。

如果您不想依赖于信息块的 ID,可以覆盖模型中的 public static iblockId() 方法,并在其中获取信息块的 ID,例如按代码。这提供了更大的灵活性,但您可能需要在数据库中进行额外的查询。

接下来,我们将像在静态上下文和动态上下文中一样使用我们的 Product 类。

添加产品

// $fields - массив, аналогичный передаваемому в CIblockElement::Add(), но IBLOCK_ID в нём можно не указывать.
$product = Product::create($fields);

注意:如果 $product 字段在以后被使用,建议立即使用数据库中的新查询更新对象。这是由于 CIblockElement::Add() 和 CIblockElement::GetList() 中的字段格式在 100% 上不匹配。这样做的方法是

$product = Product::create($fields);
$product->refresh();

更新

// вариант 1
$product['NAME'] = 'Новое имя продукта';
$product->save();

// вариант 2
$product->update(['NAME' => 'Новое имя продукта']);

不进行数据库查询实例化模型

对于一些操作,不需要从数据库获取信息,只需对象 ID 即可。在这种情况下,只需实例化对象模型,并将标识符传递给构造函数。

$product = new Product($id);

//теперь есть возможно работать с моделью, допустим
$product->deactivate();

从数据库获取对象字段

$product = new Product($id);

// метод `load` обращается к базе, только если информация еще не была получена.
$product->load();

// Если мы хотим принудительно обновить информацию из базы даже если она уже была получена ранее
$product->refresh();

// После любого из этих методов, мы можем работать с полученными полями (`echo $product['CODE'];`)

//Для текущего пользователья есть отдельный хэлпер
$user = User::current();
// В итоге мы получаем инстанс User с заполненными полями. 
// Сколько бы раз мы не вызывали `User::current()` в рамках работы скрипта, запрос в базу происходит только один раз - первый.
// `User::freshCurrent()` - то же самое, но получает данные из базы каждый раз.

所述方法将数据库中的数据保存到类模型实例中。模型对象实现了 ArrayAccess,因此可以像处理数组一样处理它们。

$product->load();
if ($product['CODE'] === 'test') {
    $product->deactivate();
}

将模型转换为数组/json

$array = $product->toArray();
$json = $product->toJson();

默认情况下,模型的所有字段都可在数组中访问,这并不总是希望的。模型有特殊的属性 protected $visible = [];protected $hidden = [];,可以通过它们来制定白名单/黑名单字段,以将模型转换为数组/json。

从数据库获取信息

使用模型最常见的情况是从数据库获取元素/列表。用于构建查询的是“Fluent API”,它在其内部使用标准 Bitrix API。

构建查询的起点是使用静态方法 ::query()。此方法返回一个查询构建器对象(ElementQuerySectionQueryUserQuery),通过该对象构建查询链。

最简单的例子

$products = Product::query()->select('ID')->getList();

实际上,这个格式更多的是为了理解,有一个更方便的格式,它使用 __callStatic 将控制权传递给查询对象。

$products = Product::select('ID')->getList();

任何查询链都必须以以下方法之一结束

  1. ->getList() - 获取对象集合(参见 https://laravel.net.cn/docs/master/collections)。默认情况下,每个元素的键是其ID。
  2. ->getById($id) - 根据ID获取对象。
  3. ->first() - 获取满足查询参数的一个(第一个)对象。
  4. ->count() - 获取对象数量。
  5. ->paginate() 或 ->simplePaginate() - 获取分页列表及其元数据(参见 https://laravel.net.cn/docs/master/pagination
  6. 针对单个实体的方法:->getByLogin($login)->getByEmail($email) - 获取具有指定登录名/email的第一个用户。 ->getByCode($code)->getByExternalId($id) - 获取第一个匹配的元素或信息块部分按CODE/EXTERNAL_ID

查询管理

  1. ->sort($array) - 相当于 $arSortCIBlockElement::GetList 的第一个参数)

示例

->sort(['NAME' => 'ASC', 'ID' => 'DESC'])

->sort('NAME', 'DESC') // = ->sort(['NAME' => 'DESC'])

->sort('NAME') // = ->sort(['NAME' => 'ASC'])

  1. ->filter($array) - 相当于 $arFilter
  2. ->navigation($array)
  3. ->select(...) - 相当于 $arSelect

示例

->select(['ID', 'NAME'])

->select('ID', 'NAME')

select() 支持两个额外的值 - 'FIELDS'(选择所有字段),'PROPS'(选择所有属性)。对于用户,还可以指定 'GROUPS'(将用户组添加到选择中)。对于 ElementModel,默认值为 ['FIELDS', 'PROPS']

  1. ->limit($int)->take($int)->page($int)->forPage($page, $perPage) - 用于导航

Fetch 和 GetNext

默认情况下,在模型内部,为了迭代从数据库中获取的元素/部分/用户,使用的是高效的方法 ->Fetch()。与 ->GetNext() 不同,它不会将数据转换为html安全格式,也不会将 DETAIL_PAGE_URL、SECTION_PAGE_URL 转换为元素和类别的真实URL。如果查询结果需要这些转换,则可以切换到该方法。

  1. 可以通过设置模型的属性来立即切换整个模型。

         public static $fetchUsing = 'GetNext';
        
         // полная форма, если нужно менять параметры.
         public static $fetchUsing = [
             'method' => 'GetNext',
             'params' => [true, true],
         ];
    
  2. 也可以为单个查询切换。

          Products::query()->filter(['ACTIVE' => 'Y'])->fetchUsing('GetNext')->getList();
          // вместо строки `'GetNext'` можно как и в первом случае использовать массив.
    

一些额外的注意事项

  1. 为限制选择添加了别名 limit($value)(对应 nPageSize)和 page($num)(对应 iNumPage
  2. 在API的一些地方比在Bitrix中更友好。例如,在用户筛选中,不必使用 'GROUP_IDS'。在传递 'GROUP_ID'(Bitrix要求的键,例如在创建用户时)或 'GROUPS' 时,结果将相同。
  3. 在创建或更新信息块元素和部分时,Bitrix会重建搜索索引,并允许跳过特定调用 Add/Update 的重建以提高性能。在模型中,您可以通过直接在类-模型中设置 protected static $updateSearch = false; 或在添加/更新之前直接调用静态方法 Product::setUpdateSearch(false) 来实现相同的效果。
  4. 对于 CIBlockElement::Add/Update,可以使用相同的方式控制 $bWorkFlow 和 $bResizePictures 标志。

查询范围

可以扩展查询构建器,在模型中添加 "query scopes"。为此,需要创建一个以 scope 开头的方法。

示例 "query scope",它已经存在于包中。

    /**
     * Scope to get only active items.
     *
     * @param BaseQuery $query
     *
     * @return BaseQuery
     */
    public function scopeActive($query)
    {
        $query->filter['ACTIVE'] = 'Y';
    
        return $query;
    }

...

$products = Product::filter(['SECTION_ID' => $secId])
                    ->active()
                    ->getList();

在 "query scopes" 中也可以传递额外的参数。


    /**
     * @param ElementQuery $query
     * @param string|array $category
     *
     * @return ElementQuery
     */
    public function scopeFromSectionWithCode($query, $category)
    {
        $query->filter['SECTION_CODE'] = $category;

        return $query;
    }

...
$users = Product::fromSectionWithCode('sale')->getList();

这些数据范围已经存在于包中,可以使用它们。

停止操作

有时需要停止从数据库中查询查询范围。为此,只需返回 false 即可。示例

    public function scopeFromCategory($query, $category)
    {
        if (!$category) {
            return false;
        }
        
        $query->filter['SECTION_CODE'] = $category;

        return $query;
    }
...

结果不会对数据库进行查询 - getList() 将返回空集合,getById() - false,而 count() - 0。

可以通过手动调用方法 ->stopQuery() 来达到相同的效果。

查询缓存

所有提到的 Bitrix 模型都拥有简单的内置缓存机制。只需在调用链中添加 ->cache($minutes)-> 即可,数据库查询结果将被缓存指定分钟。例如:$products = Products::query()->cache(30)->filter(['ACTIVE' => 'Y'])->getList()。底层缓存机制使用 d7 Bitrix 的标准机制。缓存键依赖于模型和所有查询参数。

访问器

有时需要修改在从数据库中检索数据后从模型中获取数据之前的数据。为此,使用访问器。同样,为了添加访问器,需要在相应的模型中添加方法。

方法命名规则为 $methodName = "get".camelCase($field)."Attribute"。示例


    public function getXmlIdAttribute($value)
    {
        return (int) $value;  
    }
    
    // теперь в $product['XML_ID'] всегда будет целочисленное значение
    

使用时应谨慎,因为原始值将不可用。

也可以为不存在的(虚拟)字段创建访问器,例如

    public function getFullNameAttribute()
    {
        return $this['NAME']." ".$this['LAST_NAME'];
    }
    
    ...
    
    echo $user['NAME']; // John
    echo $user['LAST_NAME']; // Doe
    echo $user['FULL_NAME']; // John Doe

为了使这些虚拟访问器在 toArray() 和 toJson() 中显示,需要明确在模型的 $appends 字段中指定它们。

    protected $appends = ['FULL_NAME'];

语言访问器

对于多语言网站,通常的做法是为每种语言创建自己的属性,例如,UF_TITLE_RU、UF_TITLE_BY。在这种情况下,可以为每种这样的字段创建访问器

// используем далее $section['UF_TITLE'];
public function getUfTitleAttribute()
{
    return $this['UF_TITLE_' . strtoupper(LANGUAGE_ID)];
}

// используем далее $element['PROPERTY_TITLE'];
public function getPropertyTitleAttribute()
{
    return $this['PROPERTY_TITLE_' . strtoupper(LANGUAGE_ID) . '_VALUE'];
}

由于这些访问器类型相同并且具有会污染模型的不愉快特性,因此可以为此使用特殊简短语法

class Product extends ElementModel
{
    protected $languageAccessors = [
        'PROPERTY_TITLE',
        'PROPERTY_FOO'
    ];
}

模型事件(Model Events)

事件允许在模型的各个生命周期点插入并执行任意代码。例如,在创建元素时自动设置字符编码。模型事件不使用 Bitrix 的事件模型(无论是旧内核还是 D7)和只涉及模型内部发生的事情。使用 Bitrix 事件覆盖了更多用例。

事件处理器通过在模型类中重定义相应方法来设置。

class News extends ElementModel
{
    /**
     * Hook into before item create or update.
     *
     * @return mixed
     */
    protected function onBeforeSave()
    {
        $this['CODE'] = CUtil::translit($this['NAME'], "ru");
    }

    /**
     * Hook into after item create or update.
     *
     * @param bool $result
     *
     * @return void
     */
    protected function onAfterSave($result)
    {
        //
    }
}

其他事件处理器的签名与上述相同。

可用事件列表

  1. onBeforeCreate - 在添加记录之前
  2. onAfterCreate(bool $result) - 在添加记录之后
  3. onBeforeUpdate - 在更新记录之前
  4. onAfterUpdate(bool $result) - 在更新记录之后
  5. onBeforeSave - 在添加或更新记录之前
  6. onAfterSave(bool $result) - 在添加或更新记录之后
  7. onBeforeDelete - 在删除记录之前
  8. onAfterDelete(bool $result) - 在删除记录之后

如果从 onBefore...() 处理器返回 return false;,则后续操作将被取消。在处理器中,可以通过当前模型对象的属性获取额外信息。例如,在 onBefore...() 处理器中,可以通过 $this->fields 访问所有字段;在所有 onAfter...() 处理器中,可以通过 $this->eventErrors 访问错误数组;在 onBeforeUpdate()onBeforeSave() 处理器中,可以通过 $this->fieldsSelectedForSave 访问将更新的字段数组。

D7 模型

与之前提到的模型略有不同的是 D7Model。与旧的 GetList 等不同,它使用 D7 作为后端。通过它,可以像操作 D7 的常规实体一样操作 D7Model,也可以操作嗨加载块。

嗨加载块的示例

class Subscriber extends D7Model
{
    public static function tableClass()
    {
        $hlBlock = HighloadBlockTable::getRowById(1);
    
        return HighloadBlockTable::compileEntity($hlBlock)->getDataClass();
    }
}

获取嗨加载块类的逻辑可以是任何形式,但重要的是不要忘记编译它,否则它将不会工作。最方便的方法是使用辅助包 https://github.com/arrilot/bitrix-iblock-helper/。使用它,我们可以得到以下内容

class Subscriber extends D7Model
{
    public static function tableClass()
    {
        return highloadblock_class('app_subscribers');
    }
}

如果我们不使用嗨加载块,而是使用完整的 D7 ORM 实体,则只需在此方法中返回该实体的完整类名即可。D7Model 的调用链和方法名称与之前模型相同。传递给这些方法的任何内容都将传递到 D7。

获取所有名为 John 的订阅者的示例,并缓存 5 分钟

$subscribers = Subscriber::query()->cache(5)->filter(['=NAME'=>'John])->getList();

完整方法列表如下

/**
 * static int count()
 *
 * D7Query methods
 * @method static D7Query runtime(array|\Bitrix\Main\Entity\ExpressionField $fields)
 * @method static D7Query enableDataDoubling()
 * @method static D7Query disableDataDoubling()
 * @method static D7Query cacheJoins(bool $value)
 *
 * BaseQuery methods
 * @method static Collection getList()
 * @method static D7Model first()
 * @method static D7Model getById(int $id)
 * @method static D7Query sort(string|array $by, string $order='ASC')
 * @method static D7Query order(string|array $by, string $order='ASC') // same as sort()
 * @method static D7Query filter(array $filter)
 * @method static D7Query addFilter(array $filters)
 * @method static D7Query resetFilter()
 * @method static D7Query navigation(array $filter)
 * @method static D7Query select($value)
 * @method static D7Query keyBy(string $value)
 * @method static D7Query limit(int $value)
 * @method static D7Query offset(int $value)
 * @method static D7Query page(int $num)
 * @method static D7Query take(int $value) // same as limit()
 * @method static D7Query forPage(int $page, int $perPage=15)
 * @method static \Illuminate\Pagination\LengthAwarePaginator paginate(int $perPage = 15, string $pageName = 'page')
 * @method static \Illuminate\Pagination\Paginator simplePaginate(int $perPage = 15, string $pageName = 'page')
 * @method static D7Query stopQuery()
 * @method static D7Query cache(float|int $minutes)
 */

有关详细信息,请参阅 vendor/arrilot/bitrix-models/src/Models/D7Model.phpvendor/arrilot/bitrix-models/src/Queries/D7Query.php

模型之间的关联(Relations)

除了处理单独的Bitrix模型外,还可以构建模型之间的关联,这使得它们可以通过主要的数据对象轻松获取。例如,商品与商品问题相关联。通过声明这种关联,您可以使用表达式 $product->questions 获取问题模型的实例,该表达式返回以 Question 类(BaseBitrixModel 的子类)对象为集合的问题信息。

声明关联

/**
 * Class Product
 *
 * @property Brand $brand
 * @property ProductQuestion $questions
 * @property Storage $storages
 */
class Product extends ElementModel
{
    ...
    
    /**
     * ID Brand записан в текущую модель в свойтво PROPERTY_BRAND_VALUE (не множественное)
     * (у товара может быть только один бренд, но у бренда много товаров)
     */
    public function brand()
    {
        return $this->hasOne(Brand::class, 'ID', 'PROPERTY_BRAND_VALUE');
    }
    
    /**
     * У ProductQuestion в свойтве PROPERTY_PRODUCT_VALUE записан ID текущей модели
     * (у товара может быть много вопросов, но вопрос относится только к одному товару)
     *
     * Но это будет так же работать, если PROPERTY_PRODUCT_VALUE будет множественным
     */
    public function questions()
    {
        return $this->hasMany(ProductQuestion::class, 'PROPERTY_PRODUCT_VALUE', 'ID');
    }
    
    /**
     * ID Storage записан в текущую модель в свойтво PROPERTY_STORAGE_VALUE (множественное)
     * (у товара может быть много складов, на складе может быть много товаров)
     */
    public function storages()
    {
        return $this->hasMany(Storage::class, 'ID', 'PROPERTY_STORAGE_VALUE');

    }
}

关联名称区分大小写。

在声明关联时,您必须提供以下信息

  • 关联的重复次数:使用 hasMany() 或 hasOne() 方法指定。
  • 关联的类名:作为 hasMany() / hasOne() 方法的第一个参数指定。
  • 两种数据类型之间的关联:第二个参数为外部模型的字段,第三个参数为内部模型(默认为 ID)的字段。
  • 与其他 ORM 不同,多重关联不使用中间(pivot)表。相反,它使用 Bitrix 的多重属性。

访问关联数据

声明关联后,您可以通过关联名称访问关联数据。通过对象属性进行访问。属性名称 = 关联方法名称(但无方法括号)

$product = Product::getById(1);

// В этот момент используются магические методы и выполняются sql запросы в БД за данными.
$product->brand; // Объект класса Brand
$product->questions; // Collection объектов класса ProductQuestion

// Запросы в базу выполняются лишь один раз. При повторном обращении к переменной возвращаются данные получененные при первом запросе. 
$product->brand;
$product->questions;

延迟加载和贪婪加载

在上面的示例中使用了延迟加载(数据在第一次访问时加载)。当处理数据数组时,会遇到 n + 1 查询问题。

$products = Product::getList();

foreach($products as $product) {
    // Выполняется запрос
    $product->brand;
}

为了避免这种情况,需要使用贪婪加载

// Выполняется один дополнительный запрос который получит все бренды для всех полученных продуктов.
$products = Product::query()->with('brand')->getList();

foreach($products as $product) {
    // Запрос не выполняется
    $product->brand;
}
  • with 方法可以调用多次。
  • 作为参数接受一个字符串 - 关联名称或多个字符串/字符串数组 ->with('brand', 'questions')/->with(['brand', 'questions'])
  • 您还可以指定嵌套关联 ->with('questions.answers')(在这种情况下,将同时加载问题和每个问题的答案列表。总共3个查询 - 商品,问题,答案)。
  • 要修改查询,可以使用回调。例如,仅加载活动问题,并为它们加载答案
    ->with([
      'questions' => function ($query) {
          $query->filter(['ACTIVE' => 'Y']);
      },
      'questions.answers'
    ])
    

使用 Eloquent 模型

包的第二部分是集成 ORM Eloquent 到 Bitrix 的用户表,即在开发过程中手动创建的,而不是随系统提供的。从本质上讲,这是直接查询数据库,D7 ORM 和本包中的 D7Model 的替代方案。

通过 Eloquent,不仅可以处理用户表,还可以处理 Highload 块,这非常方便。在这种情况下,我们与 Highload 块的表直接工作,而无需通过任何 Bitrix API。

请注意,与 Bitrix 不同,Eloquent 使用 PDO 作为 PHP 扩展来访问 mysql,而不是 mysql/mysqli。这意味着

  1. 需要安装和配置 PDO
  2. 将创建两个数据库连接。

备注:问题:为什么在一个包中使用 Eloquent,如果已经有了 D7Model?哪个更好?回答:选择它们取决于项目条件和个人偏好。Eloquent 比 Bitrix 和 D7Model 中的所有内容都更方便、功能更强大。例如,它具有通过中间表实现的完整模型关联等。另一方面,这是一个大的外部依赖,有自己的要求

缺点

安装

首先需要安装另一个依赖项 - composer require illuminate/database 之后,在 init.php 中添加另一行 - Arrilot\BitrixModels\ServiceProvider::registerEloquent(); 现在,可以创建继承自 EloquentModel 的 Eloquent 模型

<?php

use Arrilot\BitrixModels\Models\EloquentModel;

class Product extends EloquentModel
{
    protected $table = 'app_products';
}

如果表名为 products(类的复数形式名称),则可以省略 protected $table = 'products'; - 这是 Eloquent 的标准行为。非标准之处在于

  1. 主键是 ID,而不是 id
  2. 记录创建和更新时间的字段分别称为 UF_CREATED_ATUF_UPDATED_AT,而不是 created_atupdated_at

如果您决定不在表中添加字段 UF_CREATED_AT 和 UF_UPDATED_AT,则在模型中需要设置 public $timestamps = false;

通过 Eloquent 操作高负载块

假设我们已经为品牌创建了一个 Highload-块 Brands,为它指定了 brands 表,并添加了属性 UF_NAME。那么对应的模型类将是这样的

<?php

use Arrilot\BitrixModels\Models\EloquentModel;

class Brand extends EloquentModel
{
    public $timestamps = false;
}

要向其中添加新记录,只需以下代码

$brand = new Brand();
$brand['UF_NAME'] = 'Nike';
$brand->save();

// либо даже такого если настроены $fillable поля.
$brand = Brand::create(['UF_NAME' => 'Nike']);

为了充分使用 Eloquent 模型,重要的是要了解该 ORM 的官方文档 (链接再次)

最后要注意的是,尽管 Bitrix 模型和 Eloquent 模型的 API 非常相似(这在很大程度上是因为 bitrix-models 是在 Eloquent 的影响下开发的),但这毕竟是两件事,它们在内部是完全独立的。例如,不能将 Eloquent 模型与 Bitrix 模型建立关系。

高负载块和 Eloquent 模型的多重属性

高负载块的多重属性实现得有点巧妙。数据存储在两个地方

  1. 直接存储在高负载块表中,以序列化形式。
  2. 还有一个额外的表来存储这个属性的值。幸运的是,包可以很好地解决这个问题。添加多重属性时,只需在模型的 $multipleHighloadBlockFields 字段数组中添加此属性的代码即可,例如 public $multipleHighloadBlockFields = ['UF_SOME_MULTIPLE_FIELD']; 然后
  3. $model['UF_SOME_MULTIPLE_FIELD'] 将返回一个反序列化的数组
  4. 要添加/更新字段值,也只需在 $model['UF_SOME_MULTIPLE_FIELD'] 中放置一个数组,无需手动序列化。
  5. 在添加/更新字段值时,更改将自动应用于辅助表。无需手动更改它。为了使最后一点工作,需要安装额外的依赖项 - composer require illuminate/events。没有这个依赖项,辅助表将不会更新。关于它的更多信息将在下一个段落中介绍。

Eloquent 模型中的事件

Eloquent 有所谓的 模型事件 / Model events,它们允许在模型的某个工作时刻介入。整体上类似于 Bitrix 的事件 OnBeforeIblockElementUpdate 等。如果需要这些事件,则还需要安装 illuminate/database 依赖项以及 illuminate/events

composer require illuminate/events

查询构建器

在连接 Eloquent 时,我们免费获得了 Laravel 的 Query Builder https://laravel.net.cn/docs/master/queries,这对于需要直接与数据库交互,而不经过模型层抽象的情况非常有用。它比 $DB->Query() 和其他方法更方便,也更安全。

通过全局可访问的类 DB 进行构建器的操作。例如,将品牌元素添加到 HL 块的代码如下

DB::table('brands')->insert(['UF_NAME' => 'Nike']);

分页导航(pagination)

Bitrix 模型和 Eloquent 模型都支持 ->paginate()->simplePaginate()(见 https://laravel.net.cn/docs/master/pagination)。为了然后通过 ->links() 显示分页导航,需要

  1. 安装 https://github.com/arrilot/bitrix-blade/
  2. 将默认视图从 https://github.com/laravel/framework/tree/master/src/Illuminate/Pagination/resources/views 复制到 local/views/pagination

然后可以修改或创建新的视图。

连接到多个数据库,详细配置连接

默认情况下,连接到数据库使用的是与 Bitrix 一起工作的标准配置。例如,它可以这样(.settings.php 文件)

'connections' => [
    'value' => [
        'default' => [
            'className' => '\\Bitrix\\Main\\DB\\MysqliConnection',
            'host' => 'localhost',
            'database' => 'db',
            'login' => 'login',
            'password' => 'password',
            'options' => 2,
        ],
    ],
    'readonly' => true,
]

为了能够利用 Eloquent 的全部功能,必须在 .settings.php 文件中添加一个 bitrix-models.illuminate-database 数组来描述连接

[
    ...
    'bitrix-models.illuminate-database' => [
        'value' => [
                'default' => 'mysql',
                'connections' => [
                    'mysql' => [
                        'driver' => 'mysql',
                        'url' => getenv('DB_MYSQL_URL'),
                        'host' => getenv('DB_MYSQL_HOST'),
                        'port' => getenv('DB_MYSQL_PORT'),
                        'database' => getenv('DB_MYSQL_DATABASE'),
                        'username' => getenv('DB_MYSQL_USERNAME'),
                        'password' => getenv('DB_MYSQL_PASSWORD'),
                        'unix_socket' => getenv('DB_MYSQL_SOCKET'),
                        'charset' => 'utf8',
                        'collation' => 'utf8_unicode_ci',
                        'prefix' => '',
                        'prefix_indexes' => true,
                        'strict' => false,
                        'engine' => null,
                        'options' => [],
                    ],
                    'mysql_2' => [
                        ...
                    ],
                ],
            ],
        'readonly' => true,
    ],
]

重要!如果设置了 bitrix-models.illuminate-database 数组,则 Eloquent 的数据库连接设置将仅从此处获取,而标准设置将被忽略。

更多连接配置示例(connections):https://github.com/laravel/laravel/blob/5.7/config/database.php

D7Model/EloquentModel中元素的活跃性

在Bitrix的信息块中有一个ACTIVE字段,其值可以是'Y'/'N',经常用于过滤。在HiLoad块和自定义表中,默认没有这样的字段,但是这个包提供了一个帮助创建类似功能的trait。它是如何工作的

  1. 向表/HiLoad块中添加字段UF_DEACTIVATED_AT,类型为datetime。
  2. 在D7Model/EloquentModel中添加trait use \Arrilot\BitrixModels\Models\Traits\DeactivationTrait;
  3. 现在模型中可用的方法有

    3.1. $model->deactivate()$model->activate() - 在数据库中停用或激活元素。

    3.2. $model->markForDeactivation()$model->markForActivation() - 与前述相同,但仅更改PHP变量,不执行 ->save()。当需要激活时同时进行其他表更改,而不想进行额外的数据库查询时,这很有用。

    3.3. 范围方法 ->active()->deactivated()。例如 SomeD7Model::query()->active()->getList()