Detect block type in page

Hi,
I want to include a stylesheet whenever a vertain block is used. It may be used by various templates and different fields. In layouts or blocks.
Is the following the best way to check all fields of a page if they may have a block of type X?

foreach ($page->content()->fields() as $field) {
    if ($field->toBlocks()->hasType('X')){
        echo css(['media/plugins/aaa/X/css/X.css']);
        break;
    }
}

Or is there a better way?

Hm, I wonder if this should actually be limited to blocks fields (getting them from the blueprint), because you can transform any type of field to a blocks collection, even if it doesn’t make sense. It seems to default to type text. So in case x was text in your example, it would return true for every field.

Thanks @texnixe, I have to correct my reply…I misunderstood you. Yes, true. But for what I need it, I would always have a custom block name (that one of my plugin) that would very likely only really match that block.

I was just wondering if it may be a bit inefficient, as there is a Blocks object created for every field, every time… or if there was an easy way to only run it on fields of type layouts or blocks or another that may contain blocks.

As far as I can see I would have to get the blueprint from the page and check the each field:

$bpFields = $page->blueprint()->fields();
foreach ($page->content()->fields() as $field) {
    if (isset($bpFields[$field->key()])
        && in_array($bpFields[$field->key()]['type'], ['blocks', 'layout', 'object'])
        && $field->toBlocks()->hasType('X')) {
        echo css(['media/plugins/aaa/X/css/X.css']);
        break;
    }
}

Do you think this covers all places where a block might be used?

I’d do it like this to only check the relevant field types:

$fieldsToCheck = array_keys(array_filter($page->blueprint()->fields(), fn($item) => in_array($item['type'], ['blocks', 'layout'])));
foreach ($fieldsToCheck as $fieldName) {
    if ($page->{$fieldName}()->toBlocks()->hasType('text')) {
        echo 'found';
        break;
    }
}

Why did you include the object field? Do you have blocks nested inside object fields? But then filtering by block type would not work, anyway.

But I wonder if it wouldn’t make more sense to always include that css file, as it will be cached anyway, instead of implementing this rather expensive logic.

@texnixe Thanks for the improved code! And yes, maybe you are right.

Regarding the object field, it can contain blocks and layout no? I have never used it that way.

Yes, it can. Don’t know if it really makes sense. But what I was trying to say: If you call toBlocks() on an object field (myObjectField), it will not call toBlocks recursively on the nested fields inside your block.

So calling $page->myObjectField()->toBlocks() will not give you anything useful. In that case, you would have to loop through the individual fields inside that object field and call toBlocks on it.

Ok. Thanks for the clarification!