使用Rainlab Translate进行下拉菜单的高查询开销

时间:2018-01-30 10:05:52

标签: octobercms octobercms-plugins

我有一个名为Area的模型,其中包含我需要填充下拉列表的区域名称列表。该列表使用Rainlab Translate插件进行翻译。

如果我只做一个简单的Area::lists(),那么列表就不会被翻译。但是,如果我执行Area::get()->lists(),则会对其进行翻译,但会在rainlab_translate_attributes表中为下拉列表中的每个项目运行一个查询,从而导致运行约100个查询和1.5秒请求持续时间。

模型

<?php namespace Namespace\PluginName\Models;

use Model;

class Area extends Model
{
    public $implement = ['RainLab.Translate.Behaviors.TranslatableModel'];

    public $translatable = ['name'];

    // .... 
}

查看

<div class="form-group {{ errors.first('location_id') ? 'has-error' }}">
    {{ form_label('area_id','Area') }}
    {{ form_select('area_id', {'': 'Select...'} + area, null, {'class': 'form-control', 'placeholder': 'Select...'}) }}
    <small class="text-danger" data-validate-for="area_id"></small>
</div>

组件选项#1 (快速查询但项目无法翻译)

public function areas() {
    return Area::lists('name','id');
}

组件选项#2 (项目已翻译但约100次查询且非常慢)

public function areas() {
    return Area::get()->lists('name','id');
}

在其他类似的情况下,我会添加public $with = ['relation'],但rainlab_translate_attributes表似乎没有可以将Area模型与之关联的模型。

更新

我在Area.php模型中创建了以下函数:

public static function listAreas()
{
    $areas = Cache::rememberForever("all:" . App::getLocale()  , function() {
        return self::
        whereNotNull('iso3166_2')
        ->get()
        ->toArray();
    });

    return  self::makeCollection( $areas ) ;
}

public static function makeCollection ( array $models = [] )
{
    return self::hydrate( $models );
}

...然后在我的组件中,我尝试过:

$areas = Area::listAreas();&lt; - 这会立即读取缓存数据

$areas->lists('name','id');&lt; - 这会导致为集合中的每个项目生成一个新查询,这是一个查询的示例:

select * from {rainlab_translate_attributes {1}}的区域设置where {MODEL_ID {1}} {model_type {1}}

我已确认= 'th' and已正确设置为= '1275' and

3 个答案:

答案 0 :(得分:1)

您需要手动触发JOIN 我想,似乎no functionality available for collection

$locale = 'de';
$query = \HardikSatasiya\DemoTest\Models\Relation::query();

$query->select($query->getModel()->getTable() .'.*');
$query->addSelect('rainlab_translate_attributes.attribute_data');
$query->leftJoin('rainlab_translate_attributes', function($join) use ($locale, $query) {
    $join
        ->on(\Db::raw(\DbDongle::cast($query->getModel()->getQualifiedKeyName(), 'TEXT')), '=', 'rainlab_translate_attributes.model_id')
        ->where('rainlab_translate_attributes.model_type', '=', get_class($query->getModel()))
        ->where('rainlab_translate_attributes.locale', '=', $locale)
    ;
});

$data = $query->get();
$translatedArray = [];
foreach ($data as $value) {
    if(is_null($value->attribute_data)) {
        $translatedArray[$value->id] = $value->name;
    }
    else {
        $translations = json_decode($value->attribute_data);
        $translatedArray[$value->id] = $translations->name;
    }

}
dd($translatedArray);

可能会对你有帮助。

答案 1 :(得分:1)

我有相同的要求,并使用缓存解决了这个问题。如果您不想缓存查询,请忽略此答案,但我认为您应该考虑它。

1)确保您的RainLab Translator已配置,因此在使用App::getLocale()时,会返回译员的活动区域设置而不是Laravel。

2)在模型中创建一个方法以用于前端使用。目的是缓存Model / Relations&amp; amp;翻译属性。

E.g AreaModel.php

public static function listAreas()
{
       $areas = Cache::tags([  'areas' ])
            ->rememberForever(  "all:" . App::getLocale()  , function() {
                return self::
                    with(['relation_model_name']) // Fetch the Relation
                    ->get()
                    ->toArray();
            });

    return  self::makeCollection( $areas ) ;
}

public static function makeCollection ( array $models = [] )
{
    return self::hydrate( $models );
}

a)在这里,我们使用包含活动区域设置的密钥来查询查询

b)我们正在为相关模型添加with

c)我们只是缓存整个集合(No pluck / lists)并转换回雄辩的模型实例。

优点是,现在在您的组件Area::listAreas();中将返回缓存的集合,您可以像其他任何一样进行操作。

$areas = Area::listAreas(); // collection ( Area + Relation )

$dropdown = $areas->pluck('name', 'id'); // get Dropdown values for Areas...

一些考虑因素是每次记录更新,添加或删除(模型+关系)时清除缓存(删除缓存标记/密钥)。

Store Model及其关系模型Business Type的Redis缓存商店屏幕截图;

E.g of Store Model - EN E.g of Store Model - ES

更新

首先,我假设每个人都在我的例子中使用Redis道歉。原帖应该更侧重于实施。请不要像我一样复制/粘贴代码。

a)在我原来的回答中,我使用hydrate()方法发布了代码,以便从缓存的记录中创建一个现有的模型实例。令人困惑的是不是必要的,但我怀疑它与rainlab翻译的相关查询有关。 (需要确认)

b)return self::whereNotNull('iso3166_2')->get()->lists('name','id')足以缓存区域记录。

c)在我的评论中,我使用了pluck因为lists已被弃用。 pluck会返回一个集合 - 请参阅herehere

$areas = self::whereNotNull('iso3166_2')->pluck('name', 'id') ; // collection
$areas->toArray();

我还没有在十月份尝试过基于文件的缓存,并且不确定它的行为是否与Redis一致。

再次,一些注意事项;

a)请在我的帖子中all + locale与缓存标记areas相关联,将缓存密钥命名为唯一且有意义的内容。例如areas.iso3166_2.locale(避免覆盖)

b)添加Cache :: forget(&#39; key&#39;);在您的模型中afterSave&amp; afterDelete方法

c)如果您正在缓存相关模型,请在更改时清除缓存。

答案 2 :(得分:0)

另一个可能影响速度的想法(如果不是查询数量) - 索引模型的名称属性:

public $translatable = [
    ['name', 'index' => true]
];

参考:https://github.com/rainlab/translate-plugin#indexed-attributes