Answers.Body: <p>If you weren’t using Element API here, you could do this by adding a condition to the element query:</p>
  
  
  
  <pre class="lang-php prettyprint-override"><code>$query-&gt;andWhere(['exists', (new \craft\db\Query())
      -&gt;from(['b' =&gt; \craft\db\Table::MATRIXBLOCKS])
      -&gt;innerJoin(\craft\db\Table::MATRIXBLOCKTYPES . ' bt', '[[bt.id]] = [[b.id]]')
      -&gt;where('[[b.ownerId]] = [[elements.id]]')
      -&gt;andWhere(['bt.handle' =&gt; 'yourBlockTypeHandle'])
  ]);
  </code></pre>
  
  <p>But Element API doesn’t give you direct access to the query. I can think of a couple ways to pull it off, though.</p>
  
  <hr>
  
  <p><strong>Option 1: Implement a custom element query param.</strong></p>
  
  <p>You could define a new element query behavior class, which wraps that condition into a new <code>hasBlockType</code> param:</p>
  
  <pre class="lang-php prettyprint-override"><code>&lt;?php
  
  use craft\db\Query;
  use craft\db\Table;
  use craft\elements\db\ElementQuery;
  use craft\events\CancelableEvent;
  use yii\base\Behavior;
  
  /**
   * @property ElementQuery $owner
   */
  class ElementQueryBehavior extends Behavior
  {
      public $hasBlockType;
  
      public function hasBlockType(string $handle)
      {
          $this-&gt;hasBlockType = $handle;
      }
  
      public function events()
      {
          return [
              ElementQuery::EVENT_BEFORE_PREPARE =&gt; [$this, 'prepare'],
          ];
      }
  
      public function prepare()
      {
          if ($this-&gt;hasBlockType) {
              $this-&gt;owner-&gt;andWhere(['exists', (new Query())
                  -&gt;from(['b' =&gt; Table::MATRIXBLOCKS])
                  -&gt;innerJoin(Table::MATRIXBLOCKTYPES . ' bt', '[[bt.id]] = [[b.id]]')
                  -&gt;where('[[b.ownerId]] = [[elements.id]]')
                  -&gt;andWhere(['bt.handle' =&gt; $this-&gt;hasBlockType])
              ]);
          }
      }
  }
  </code></pre>
  
  <p>Then you’d need to register that behavior with new element query instances, from your <a href="https://docs.craftcms.com/v3/extend/module-guide.html" rel="nofollow noreferrer">module’s</a> <code>init()</code> method.</p>
  
  <pre class="lang-php prettyprint-override"><code>\craft\base\Event::on(
      \craft\elements\db\ElementQuery::class,
      \craft\db\Query::EVENT_DEFINE_BEHAVIORS,
      function(\craft\events\DefineBehaviorsEvent $event) {
          $event-&gt;behaviors['my-behavior'] = new ElementQueryBehavior();
      }
  );
  </code></pre>
  
  <p>Finally, from <code>config/element-api.php</code>, add this to your endpoint’s <code>criteria</code> array:</p>
  
  <pre class="lang-php prettyprint-override"><code>'criteria' =&gt; [
      'hasBlockType' =&gt; 'yourBlockTypeHandle',
      // ...
  ],
  // ...
  </code></pre>
  
  <p>The advantage of going this route is that your new <code>hasBlockType</code> query param will be available everywhere, so you can reuse it from any element queries you’re writing in Twig or PHP.</p>
  
  <hr>
  
  <p><strong>Option 2: Use a custom Fractal resource class.</strong></p>
  
  <p>Element API allows you to define <a href="https://fractal.thephpleague.com/resources/" rel="nofollow noreferrer">Fractal resource</a> class using the <a href="https://github.com/craftcms/element-api#class" rel="nofollow noreferrer"><code>class</code></a> endpoint param.</p>
  
  <p>Yours could extend the built-in <a href="https://github.com/craftcms/element-api/blob/v2/src/resources/ElementResource.php" rel="nofollow noreferrer">ElementResource</a> class, overriding its <a href="https://github.com/craftcms/element-api/blob/v2/src/resources/ElementResource.php#L161-L169" rel="nofollow noreferrer">getElementQuery()</a> method.</p>
  
  <pre class="lang-php prettyprint-override"><code>&lt;?php
  
  use craft\db\Query;
  use craft\db\Table;
  use craft\elementapi\resources\ElementResource;
  use craft\elements\db\ElementQueryInterface;
  
  class MyElementResource extends ElementResource
  {
      public $hasBlockType;
  
      protected function getElementQuery(): ElementQueryInterface
      {
          $query = parent::getElementQuery();
  
          if ($this-&gt;hasBlockType) {
              $query-&gt;andWhere(['exists', (new Query())
                  -&gt;from(['b' =&gt; Table::MATRIXBLOCKS])
                  -&gt;innerJoin(Table::MATRIXBLOCKTYPES . ' bt', '[[bt.id]] = [[b.id]]')
                  -&gt;where('[[b.ownerId]] = [[elements.id]]')
                  -&gt;andWhere(['bt.handle' =&gt; $this-&gt;hasBlockType])
              ]);
          }
  
          return $query;
      }
  }
  </code></pre>
  
  <p>Then from <code>config/element-api.php</code>, add this to your endpoint config (note that it would <strong>not</strong> go in <code>criteria</code>):</p>
  
  <pre class="lang-php prettyprint-override"><code>'class' =&gt; MyElementResource::class,
  'hasBlockType' =&gt; 'yourBlockTypeHandle',
  // ...
  </code></pre>
  
  <hr>
  
  <p><strong>Option 3: Just use Preparse</strong></p>
  
  <p>If you only care about a single block type, and don’t really need this to be configurable, the simplest route would be to use the <a href="https://plugins.craftcms.com/preparse-field" rel="nofollow noreferrer">Preparse Field</a> plugin, and actually store whether entries have the block type, as part of their entry data.</p>
  
  <p>For example, if the handle of the Matrix block type you care about is <code>text</code>, then you’d create a Preparse field called <code>hasTextBlock</code>, with this template:</p>
  
  <p>
  
  <pre class="lang-php prettyprint-override"><code>{{ element.myMatrixField.type('text').exists() ? '1' }}
  </code></pre>
  
  <p>Then resave your entries, and add this to your endpoint’s <code>criteria</code> array:</p>
  
  <pre class="lang-php prettyprint-override"><code>'criteria' =&gt; [
      'hasTextBlock' =&gt; '1',
      // ...
  ],
  // ...
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2023-06-05T08:32:40.990Z
Answers.Text: I think the inner join should look like this:
  
  
  `->innerJoin(Table::MATRIXBLOCKTYPES . ' bt', '[[bt.id]] = [[b.typeId]]')`
Answers.UserDisplayName: Harald Atteneder
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-06-14T12:58:47.650Z
Answers.LastActivityDate: 2019-06-14T12:58:47.650Z
Answers.UserDisplayName: Brandon Kelly
Body: <p>I'm using Element API and trying to create a query that returns a list of entries based on if the entry is using a specific Matrix Block Type.</p>
  
  <p>I've tried a few different things including a query that does a search where the field contains any value, but it's not returning any entries.</p>
  
Tags: <matrix><plugin-elementapi>
Title: How can I query entries based on a Matrix Block Type?
ThreadText: # How can I query entries based on a Matrix Block Type?
  
  (Asked by: username_0 on 2019-06-14)
  
  I'm using Element API and trying to create a query that returns a list of entries based on if the entry is using a specific Matrix Block Type.
  
  I've tried a few different things including a query that does a search where the field contains any value, but it's not returning any entries.
  
  **Tags:** matrix, plugin-elementapi
  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_1 on 2019-06-14. Score: 2 | Accepted answer
  
  If you weren’t using Element API here, you could do this by adding a condition to the element query:
  
  ```php
  $query->andWhere(['exists', (new \craft\db\Query())
      ->from(['b' => \craft\db\Table::MATRIXBLOCKS])
      ->innerJoin(\craft\db\Table::MATRIXBLOCKTYPES . ' bt', '[[bt.id]] = [[b.id]]')
      ->where('[[b.ownerId]] = [[elements.id]]')
      ->andWhere(['bt.handle' => 'yourBlockTypeHandle'])
  ]);
  
  ```
  
  But Element API doesn’t give you direct access to the query. I can think of a couple ways to pull it off, though.
  
  ---
  
  **Option 1: Implement a custom element query param.**
  
  You could define a new element query behavior class, which wraps that condition into a new `hasBlockType` param:
  
  ```php
  <?php
  
  use craft\db\Query;
  use craft\db\Table;
  use craft\elements\db\ElementQuery;
  use craft\events\CancelableEvent;
  use yii\base\Behavior;
  
  /**
   * @property ElementQuery $owner
   */
  class ElementQueryBehavior extends Behavior
  {
      public $hasBlockType;
  
      public function hasBlockType(string $handle)
      {
          $this->hasBlockType = $handle;
      }
  
      public function events()
      {
          return [
              ElementQuery::EVENT_BEFORE_PREPARE => [$this, 'prepare'],
          ];
      }
  
      public function prepare()
      {
          if ($this->hasBlockType) {
              $this->owner->andWhere(['exists', (new Query())
                  ->from(['b' => Table::MATRIXBLOCKS])
                  ->innerJoin(Table::MATRIXBLOCKTYPES . ' bt', '[[bt.id]] = [[b.id]]')
                  ->where('[[b.ownerId]] = [[elements.id]]')
                  ->andWhere(['bt.handle' => $this->hasBlockType])
              ]);
          }
      }
  }
  
  ```
  
  Then you’d need to register that behavior with new element query instances, from your [module’s](https://docs.craftcms.com/v3/extend/module-guide.html) `init()` method.
  
  ```php
  \craft\base\Event::on(
      \craft\elements\db\ElementQuery::class,
      \craft\db\Query::EVENT_DEFINE_BEHAVIORS,
      function(\craft\events\DefineBehaviorsEvent $event) {
          $event->behaviors['my-behavior'] = new ElementQueryBehavior();
      }
  );
  
  ```
  
  Finally, from `config/element-api.php`, add this to your endpoint’s `criteria` array:
  
  ```php
  'criteria' => [
      'hasBlockType' => 'yourBlockTypeHandle',
      // ...
  ],
  // ...
  
  ```
  
  The advantage of going this route is that your new `hasBlockType` query param will be available everywhere, so you can reuse it from any element queries you’re writing in Twig or PHP.
  
  ---
  
  **Option 2: Use a custom Fractal resource class.**
  
  Element API allows you to define [Fractal resource](https://fractal.thephpleague.com/resources/) class using the [`class`](https://github.com/craftcms/element-api#class) endpoint param.
  
  Yours could extend the built-in [ElementResource](https://github.com/craftcms/element-api/blob/v2/src/resources/ElementResource.php) class, overriding its [getElementQuery()](https://github.com/craftcms/element-api/blob/v2/src/resources/ElementResource.php#L161-L169) method.
  
  ```php
  <?php
  
  use craft\db\Query;
  use craft\db\Table;
  use craft\elementapi\resources\ElementResource;
  use craft\elements\db\ElementQueryInterface;
  
  class MyElementResource extends ElementResource
  {
      public $hasBlockType;
  
      protected function getElementQuery(): ElementQueryInterface
      {
          $query = parent::getElementQuery();
  
          if ($this->hasBlockType) {
              $query->andWhere(['exists', (new Query())
                  ->from(['b' => Table::MATRIXBLOCKS])
                  ->innerJoin(Table::MATRIXBLOCKTYPES . ' bt', '[[bt.id]] = [[b.id]]')
                  ->where('[[b.ownerId]] = [[elements.id]]')
                  ->andWhere(['bt.handle' => $this->hasBlockType])
              ]);
          }
  
          return $query;
      }
  }
  
  ```
  
  Then from `config/element-api.php`, add this to your endpoint config (note that it would **not** go in `criteria`):
  
  ```php
  'class' => MyElementResource::class,
  'hasBlockType' => 'yourBlockTypeHandle',
  // ...
  
  ```
  
  ---
  
  **Option 3: Just use Preparse**
  
  If you only care about a single block type, and don’t really need this to be configurable, the simplest route would be to use the [Preparse Field](https://plugins.craftcms.com/preparse-field) plugin, and actually store whether entries have the block type, as part of their entry data.
  
  For example, if the handle of the Matrix block type you care about is `text`, then you’d create a Preparse field called `hasTextBlock`, with this template:
  
  ```php
  {{ element.myMatrixField.type('text').exists() ? '1' }}
  
  ```
  
  Then resave your entries, and add this to your endpoint’s `criteria` array:
  
  ```php
  'criteria' => [
      'hasTextBlock' => '1',
      // ...
  ],
  // ...
  
  ```
  
  > **Comments:**
  > **username_2**: I think the inner join should look like this:   `->innerJoin(Table::MATRIXBLOCKTYPES . ' bt', '[[bt.id]] = [[b.typeId]]')`  
  
  ---
  
  