<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>

CC BY-SA 4.0
2023-06-05T08:32:40.990Z
I think the inner join should look like this:


`->innerJoin(Table::MATRIXBLOCKTYPES . ' bt', '[[bt.id]] = [[b.typeId]]')`
Harald Atteneder
CC BY-SA 4.0
2019-06-14T12:58:47.650Z
2019-06-14T12:58:47.650Z
Brandon Kelly
<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>

<matrix><plugin-elementapi>
How can I query entries based on a Matrix Block Type?
# 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:
$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:
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 init() method.
\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:
'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 class using the class endpoint param.
Yours could extend the built-in ElementResource class, overriding its getElementQuery() method.
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):
'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 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:

{{ element.myMatrixField.type('text').exists() ? '1' }}

Then resave your entries, and add this to your endpoint’s criteria array:
'criteria' => [
    'hasTextBlock' => '1',
    // ...
],
// ...

> **Comments:**
> **username_2**: I think the inner join should look like this:   `->innerJoin(Table::MATRIXBLOCKTYPES . ' bt', '[[bt.id]] = [[b.typeId]]')`  

---

