Php code in a partial

My layouts/home.htm has this :

        <!-- Header -->
        {% partial 'site/header' %}

        <!-- Nav -->
        {% partial 'site/nav-links' %}

        <!-- Marquee -->
        {% partial 'site/marquee' %}

partials/site/marquee.htm :

[collection announcements]
handle = "Announcement"
==
<?php
function onStart()
{
    dd($this->announcements);
}
?>
==
<div>
code
</dob>

dd returns null even though I added content in the backend - even for other Tailors like events etc.
So a partial can’t have this ?

[collection announcements]
handle = "Announcement"
==
<?php
function onStart()
{
    dd($this->announcements);
}
?>
==

How else do I have access

Hi @anjanesh

can you share more about your tailor blueprint ?

I got it solved (using Gemini) by placing this in the home.htm layout :

##
description = "Default layout"

[collection announcements]
handle = "Announcement"
==

And in themes/demo/partials/site/marquee.htm

==
<?php
function onStart()
{
    if ($this->announcements)
    {
        $this['activeAnnouncements'] = $this->announcements
        ->where('is_active', true)
        ->orderBy('priority', 'asc')
        ->get();
    }
    else
    {
        $this['activeAnnouncements'] = collect([]);
    }

    $this['activeAnnouncements'] = $this->announcements;
}
?>
==
{% if activeAnnouncements|length > 0 %}
<code>
{% endif %}

It seems

[collection announcements]
handle = "Announcement"

can’t be placed in a partial like themes/demo/partials/site/marquee.htm ?

this is my announcement.yaml file :

uuid: xxx-xxx-xxx-xxx-xxx
handle: Announcement
type: structure
name: Announcement
drafts: false

fields:
    message:
        label: Announcement Message
        type: text
        placeholder: 'Announcement Message'
        validation:
            - required

    is_active:
        label: Active
        type: switch
        default: true

    priority:
        label: Display Order
        type: number
        default: 100

columns:
    message:
        label: Message
        searchable: true

    is_active:
        label: Active
        type: switch

navigation:
    icon: icon-bullhorn
    order: 700

My composer.json :

    "require": {
        "php": "^8.5.0",
        "october/rain": "^4.0",
        "laravel/framework": "^12.0",
        "october/all": "^4.0"
    },

I thought I was using October CMS 4.1

The onStart method of a partial is executed before the components of a partial have been run (see Partials - October CMS - 4.x)

So if you place the collection component in your partial, use the onEnd method to process the announcement. Alternatively, you can also execute queries on collections directly in the page:

Cheers
Marco

1 Like

But when I place everything in the partial instead its still null.
partials/site/marquee.htm

[collection announcements]
handle = "Announcement"
==
<?php
function onEnd()
{
   // code
}
?>
==
{% if activeAnnouncements|length > 0 %}
<code>
{% endif %}

Sorry for the late reply!

Looks like components in partials are handled slightly different, and a main difference seems to be where the injected variables are placed.

You should have access to your records throught the component:

function onEnd() {
    $announcements = $this->page['announcements']->announcement;
    if(announcements) {
       ...
    }
    $this['activeAnnouncements'] = $this->announcements;
}

If you can’t access, try shopping around with a bunch of dd($this) and dd($that) (jk), I am sure you will finde something :wink:

Sorry for the half-assed initial reply. I usually don’t rely on page variables because I am afraid of name collisions.

Then how do you normally approach this ?

I usually expose the data of a component through public variables and access them via the component. Either with the alias / __SELF__ inside component partials or accessing the component through the page if dealing with a page. I try to scope variables as much as possible, to be able to add a component multiple times (don’t need it as much, but it’s easier for me to just do it always like that).

In your case, something like this should work:

[collection announcements]
handle = "Announcement"
==
<?php
function onEnd()
{
    if(!$collection = \Arr::get($this->components, 'announcements')) {
        return;
    }

    $this['activeAnnouncements'] = $collection->getPrimaryRecordQuery()
        ->where('is_active', true)
        ->orderBy('priority', 'asc')
        ->when(BackendAuth::check(), function($query) {
            $query->withDrafts();
        })
        ->get();
}
==

{% for announcement in activeAnnouncements %}
    {{ _self.renderAnnouncement(announcement) }}
{% endfor %}

{% macro renderAnnouncement(announcement) %}
    <h3>{{ announcement.title }} </h3>
    <p class="lead">{{ announcement.lead }}</p>
    {{ announcement.content|raw }}
{% endmacro %}

I sprinkled in some other code - like checking if the current site visitor is authenticated in the backend and then load drafts, render the annoucements with a macro - to show the flexiblity and how I usually like to do stuff like that.

And yes, in this case I still use a variable injected to the partial, but it is scoped to the partial (not accessible in the page). And I usualy prefix variable names with something, just to make the chance smaller that it gets overriden. That basically never happens, but it is the most annoying and debug-hours-wasting thing if it does.

And I just realised, that my initial reply and code suggestion was sloppy and incomplete. Sorry for that!

2 Likes