web-dev-qa-db-ja.com

ビューのキャッシュコンテキストを変更する

ユーザーキャッシュコンテキストをビューに設定したいのですが、これを行う場所がわかりません(どのフックで?)。ビューの設定では、キャッシュは「タグベース」に設定されています

Advanced settings of the Views

各ユーザーには特定のプロパティがあり、ビューのキャッシュを完全に無効にすることなく、ビューでそれを処理したいと思っています。

私はすでに成功せずにこのフックをチェックしています:

hook_entity_build_defaults_alter hook_entity_views_data_alter hook_views_pre_views

各フックで、$ view-> element [#cache] [contexts]配列を変更しました。


私は私の問題についていくつかの説明を与えます。

「製品」コンテンツタイプがあります。このコンテンツタイプには、「Family product」と呼ばれる分類法への参照フィールドと「Public price」番号フィールドがあります。私の目標は、「あなたの価格」というユーザーの計算された価格を表示することです。 「あなたの価格」は計算された値なので、フィールドではありません。この値を計算するために、「割引グリッド」と呼ばれる別のコンテンツタイプがあります。このコンテンツタイプにform_alterを使用して、「ファミリー製品」の各用語をリストし、それらを管理者がこのグリッドに付与する割引値のパーセントを取るテキストフィールドに関連付けます。

最後に、Userエンティティには「割引グリッド」コンテンツへの参照フィールドがあります。ユーザーと割引グリッドの関連付けにより、「製品」に属する家族に応じて「あなたの価格」を計算できます。

簡単に説明できませんでした。

それで、私はこの質問を開いて以来、多くのことを試みましたが、成功しませんでした。ビューで計算値を表示するために、現在のユーザーの(割引グリッド)を使用して計算された「あなたの価格」値を表示するカスタムビューフィールドを作成しました(query() 関数は空で、ビューがデータベースのフィールド値を検索しないようにします)。それは魅力のように働いています。

/**
 * @ingroup views_field_handlers
 * @ViewsField("node_costumer_price")
 */
class NodeCostumerPrice extends FieldPluginBase {

    /**
     * @{inheritdoc}
     */
    public function query() {
        // Leave empty to avoid a query on this field.
    }

    /**
     * @{inheritdoc}
     * @param \Drupal\views\ResultRow $values
     */
    public function render(ResultRow $values) {

        //Called a custom service who return the current User Discount grid
        $grid = \Drupal::service('discount.service')->getGrid();
        $node = $values->_entity;
        $price = "";

        $family = $node->get('field_family')->getValue();
        $publicprice = $node->get('field_public_price')->getValue();
        /* doing some calculation here */
        return $this->t(number_format((float) $price, 2, ',', '') . ' €');
    }
}

問題はキャッシュです。計算された値は、異なる割引グリッドを持つ2人のユーザー間で常に同じ価格を表示しています。

カスタムキャッシュコンテキスト「割引」を作成しました。このコンテキストは、現在のユーザーの割引グリッドのノードIDと一致します。このコンテキストは、単純なレンダー配列で非常にうまく機能します(カスタムコントローラーでレンダー配列を返すことがテストされています)。

#modules/custom/discount/src/Context/DiscountCacheContext.php

/**
 * Defines the DiscountCacheContext service, for "per discount grid" caching.
 * Cache context ID: 'discount'.
 */
class DiscountCacheContext implements CacheContextInterface {
    /**
     * The account object.
     *
     * @var \Drupal\Core\Session\AccountInterface
     */
    protected $user;

    /**
     * Constructs a new UserCacheContextBase class.
     *
     * @param \Drupal\Core\Session\AccountInterface $user
     *   The current user.
     */
    public function __construct(AccountInterface $user) {
        $this->user = $user;
    }

    /**
     * {@inheritdoc}
     */
    public static function getLabel() {
        return t('Discount');
    }

    /**
     * {@inheritdoc}
     */
    public function getContext() {
        $user = User::load($this->user->id());
        return empty($user->get('field_auto_part_discount_grid')->getValue()) ? 0 :
          $user->get('field_auto_part_discount_grid')->getValue()['0']['target_id'];
    }

    /**
     * {@inheritdoc}
     */
    public function getCacheableMetadata() {
        return new CacheableMetadata();
    }
}

現在、ビューにこのカスタムコンテキストキャッシュを「移植」する方法を検索しています。最後に、キャッシュコンテキストを挿入するためのカスタムビューフィルターハンドラーを作成しましたが、成功しませんでした。 (core/modules/user/src/Plugin/views/filter/Currentクラスを再現し、それを私のカスタムキャッシュコンテキストに適合させることによって)

今回は十分に明確にしてほしい。今週は、ビューのキャッシュを無効にすることなく解決策を見つけるように努めています(レンダリング時間/ 2)。

1
Gaius

ありがとう、@ oknate、私はあなたの答えに基づいて、必要な正確な解決策を見つけました。

ユーザーIDを使用してキャッシュタグを変更するように勧められました。このソリューションは理想的ではありません。別のユーザーがコンテンツを更新するたびにキャッシュが無効になり、キャッシュを再生成する必要があるためです(多くの処理が必要)。

しかし、あなたの答えの次の部分は、ユーザーの価格をレンダー配列として変換するというアイデアを私に与えました(以前はそれについて考えていませんでした)。このレンダーアレイに、カスタムの「割引」キャッシュコンテキストを渡しました。ビューの結果には、割引グリッドごとにキャッシュバージョンがあります。このコンテキストでは、2人のユーザーが同じグリッドを共有する場合、同じキャッシュ結果が両方のユーザーにヒットします。魔女が私の目標です!

       $user_price = number_format((float) $price, 2, ',', '') . ' €';

        return [
          '#markup' => $user_price,
          '#cache' => ['contexts' => ['discount']]
        ];

ちなみに、私はt()関数を削除しました。ここでは完全に役に立ちません(Webで見つかったチュートリアルの残りの部分)。

3
Gaius

ユーザーのfield_auto_part_discount_gridに基づいて、ビューをユーザーごとにカスタマイズする必要があるので、ビューのキャッシュ配列にユーザータグを追加できます(まだ存在しない場合)。価格に基づいてカスタムキャッシュタグを作成することもできます。

function mymodule_views_pre_view(ViewExecutable $view, $display_id, array &$args) {

  if ($view->id() == 'user_price') {
    $user_id = args[0];
    $user_price = 12345; // replace with logic to get price
    $view->element['#cache']['tags']['user:' . $user_id, 'discount:' . $user_price];
  }
}

また、ビューのレンダー配列内からのキャッシュタグはビューにバブルアップするため、NodeCostumerPriceのレンダー関数にユーザーキャッシュタグを追加できます。

の代わりに

return $this->t(number_format((float) $price, 2, ',', '') . ' €');

これを行う:

$user_price = $this->t(number_format((float) $price, 2, ',', '') . ' €');

return [
  '#markup' => $user_price,
  '#cache' => ['tags' => ['user:'.$this->user->id(), 'discount:' . $user_price]]
];

その後、これはバブルアップしてレンダーキャッシュのビューキャッシュタグに追加され、ユーザーが更新されるとキャッシュが無効になります。

カスタム価格に基づいてカスタムタグを使用する場合は、ユーザーの代わりにカスタムキャッシュタグを追加できます。

次に、hook_entity_updateで、好きなロジックに基づいてキャッシュタグを無効化できます。たとえば、ユーザーが保存されている場合、カスタムキャッシュタグ「割引」を無効にすることができます。

/**
 * Implements hook_entity_update().
 */
function mymodule_entity_update($entity) {
  if ($entity instanceof User && $entity->field_public_price->isEmpty() === FALSE) {
    $user_price = 12345; // replace with logic to get price.
    Cache::invalidateTags(array('discount:' . $user_price));
  }
}
9
oknate

ビューで使用するキャッシュコンテキストまたはキャッシュタグを構成することはできません。これは、ビューで使用されているすべてのキャッシュタグとコンテキスト、フィールド、フィルター、表示、コンテキスト、関係などを計算することで取得します。

タグのキャッシュを有効にし、現在のユーザーをコンテキストとしてどこかで使用した場合(ユーザーごとに異なるものを実際に印刷する場合)、これを自動的に取得する必要があります。そうでない場合は、ハンドラを作成してper userデータを出力する必要があります。これにより、::getCacheContextsを使用して適切なキャッシュコンテキストが定義され、これを使用するビューが自動的にユーザーコンテキストを取得します。

Drupalのキャッシングの背後にある賢いことは、一般的な共通のキャッシュを使用できる部分にアイテムをマージすることです。 Drupal 8は、キャッシュにプレースホルダーを使用することで、いくつかの巧妙なアイデアも持っています。そのため、ページのレンダリングされたhtmlをユーザー名のプレースホルダーでキャッシュし、ログインしたユーザーに基づいてユーザー名を単純に入れ替えることができます。この機能を調べます。

5
googletorp