• Skip to main content
  • Skip to primary sidebar
Martin Taylor

Martin Taylor

PHP, CSS, MySQL and WordPress tips and techniques

PHP

Populating Alpine data in a PHP page (part 1)

Posted on March 2, 2024

In a traditional PHP page, the server-side code gathers the data (e.g. from a database) and inserts it into the HTML. The browser needs to do nothing more than render the HTML it receives.

Now introduce Alpine JS to the page. Let’s say we have our data state defined like this:

 Alpine.data('mydata',() => ({
    student: {name: null, age: null},
    course: {name: null, location: null},
 }))

OK, but we want these two objects to be initialised with the student and course data from the server, so that it can be used, for example in x-show or x-bind.

We certainly don’t want to go back to the server, maybe to get the data from an API. We already got the data while the page was being built. So we need to put that data in the page somewhere that Alpine can use.

One way to do it would be for PHP to generate the data … something like:

 Alpine.data('mydata',() => ({
    student: <?php echo $student_object; ?>,
    course: <?php echo $course_object; ?>,
 }))

That would work, but often enough the data isn’t defined inline. It’s held in a standalone JS file, so it’s awkward to use PHP to fill in the gaps.

An effective solution is to provide the data as JSON, in a script element on the page. Then we just have to parse the JSON data and copy it into the Alpine data properties.

So – step 1: Generate the script element.

<?php
$student = get_student();
$course = get_course();
$jdata = [
  'student' => $student,
  'course' => $course,
];
?>
<script type="application/json">
<?php echo json_encode($jdata); ?>
</script>

Step 2 is to grab the script element, parse the JSON and copy over to the Alpine properties. There are plenty of ways to do this, but I wanted something I could reuse, so I put together a custom Alpine ‘x-json’ directive.

To use it, we just add x-json to the script element. In this most basic usage, the directive takes the objects it finds in the script element, and copies them over to the same-named objects in the Alpine data.

<script type="application/json" x-json>
<?php echo json_encode($jdata), PHP_EOL; ?>
</script>

In our example, this will take the student and course objects from the script, and copy them to the student and course properties in Alpine data.

The directive is capable of rather more than that, but I’ll describe it in full in Part 2.

Meantime, here’s the directive code.

document.addEventListener('alpine:init', () => {
  Alpine.directive('json', (el, { expression, modifiers, value }, { evaluate }) => {
    const jdata = JSON.parse(el.innerText)
    if (!modifiers.length) modifiers = Object.keys(jdata)
    const dest = value ? Alpine.store(value) : Alpine.$data(el)
    for (let m of modifiers) {
      dest[m] = jdata[m]
    }
    if (expression) evaluate (expression)
  })
})

Filed under: Alpine, PHP

PHP Calendar Class using relative dates

Posted on July 16, 2021

After correcting the error in my calendar class (see PHP Calendar Class revisited), I’d been wondering whether the code could be further simplified. Some of the calculations were a bit on the arcane side, even if they now worked OK.

The solution lay in PHP’s relative date formats, which can be used when instantiating or modifying a DateTime object.

Given that we always display full weeks (Monday to Sunday), the two key dates needed for the calendar are the first Monday to display, and the last Sunday to display. It’s more than likely that these are in the previous / next months, rather than the month in focus. For a July 2021 calendar, for example, the first Monday is June 28; and the last Sunday is August 1.

It turns out that PHP is eyebrow-raisingly helpful at deriving these two dates.

For the first Monday, what we need is the Monday that precedes the second day in the month. The first day of the month might itself be a Monday, so we allow for that by starting at the second day. Here’s an example, which shows how straightforward it is, and also that we can chain the function calls on a single line of code:

$year = 2021;
$month = 7;
$start = new DateTime();
$start->setDate($year, $month, 2)->modify('previous Monday');

$start is now set to June 28; and exactly as you’d hope, when we try it for November, $start is set to November 1. I’ve no idea what machinations are going on behind the scenes but the point is that my code is radically more simple and understandable. If there is a performance hit, it’s negligible, given that I’m only incurring it once per page.

Here’s how we get the last Sunday to be displayed. We need the first Sunday after the last-but-one day in the month. Note that setDate() conveniently allows you to have negative days, so we first add 1 to the month, and then use -1 as the day number, which steps back two days from day 1 of that month.

$end = new DateTime();
$end->setDate($year, $month + 1, -1)->modify('first Sunday');

Note that PHP uses “first” here to get the first Sunday after the date, which is perfect for our needs.

I’ll be updating my calendar class to use this new approach.

Filed under: PHP

PHP Calendar Class revisited

Posted on July 14, 2021

I found today that my PHP Calendar Class didn’t cope at all well with months that span more than five weeks. August 2021 is a good example – it covers six Monday-to-Sunday weeks, and my code was assuming that it would only need to worry about four or five such weeks.

I’ve now updated the code in the original post.

Filed under: PHP

MySQL – retrieving rows with one column as key

Posted on June 13, 2020

Getting results back from MySQL as an array is of course easy enough, using fetch_all in MySQLi or fetchAll in PDO. You get back an array of rows, and you can loop through it as necessary.

This result array is numerically indexed, from element 0 upwards, reflecting the ORDER BY clause in the query.

Element 0 = array of columns from first row
Element 1 = array of columns from second row, and so on

Fair enough, and often that’s exactly what you want, but what if you want to use the array as a lookup? I’ve encountered that situation several times, where I have a table with key / value pairs, for example, and I want to bring it into memory so that I can use it in my PHP code. Something like this, for example:

Region IDRegion name
EUEurope
USUnited States of America
APAsia Pacific

PDO provides a really useful option to handle this. The code is something like this:

$sql = 'SELECT region_id, region_name FROM regions';
$result = $this->db->query($sql)->fetchAll(PDO::FETCH_KEY_PAIR);

Note the PDO::FETCH_KEY_PAIR option – it causes the array of rows to be a perfectly formed key/value structure:

[
“EU” => “Europe”
“US” => “United States of America”
“AP” => “Asia Pacific”
]

And now I can use the array as a lookup.

Just recently, though, I came up against a need to have several columns in the lookup array. The situation was a query using GROUP BY, returning counts and sums from a database table based on a number of criteria.

So I wanted a lookup array with a key (as above), but with several columns in each element. For simplicity, let’s say we have a third column (Manager) in the region table, so the query is this:

SELECT region_id, region_name, manager FROM regions

Now, FETCH_KEY_PAIR can’t do this. It will only handle a query with two columns – one to be the key and one to be the value. Well, maybe it could … you might use CONCAT_WS, so that there are only two fields:

SELECT region_id, CONCAT_WS(‘,’ region_name, manager) FROM regions

But let’s stick with the original query. I’d started out by looping through the result and building a second indexed array. It worked, but it looked untidy so I looked around for a more concise approach. The answer, it turns out, is PHP’s array_column() function, which includes an option to reindex an array by one of its columns. Perfect for this situation!

So we code things like this:

$sql = 'SELECT region_id, region_name, manager FROM regions';
$result = $this->db->query($sql)->fetchAll(PDO::FETCH_ASSOC);
$regions = array_column($result, null, 'region_id');

And the resulting $regions array is structured like this:

[
“EU” => [“region_id”=>”EU”, “region_name”=>”Europe”, “manager”=>”Alicia”]
“US” => [“region_id”=>”US”, “region_name”=>”United States of America”, “manager”=>”Tony”]
“AP” => [“region_id”=>”AP”, “region_name”=>”Asia Pacific”, “manager”=>”Leanne”]
]

Note how the region id remains in the array of columns on each row, but that’s a minor inconvenience – the result is what I needed.

Filed under: MySQL, PHP

CSS-only menus in WordPress

Posted on May 26, 2020

I recently set myself the challenge of building a CSS-only menu (including sub-menu dropdowns) for a WordPress site. Why? Partly as an academic exercise, but partly in an attempt to reduce what appeared to be a too-busy combination of CSS, JS and PHP in the site and theme as it stood.

All this of course in contravention of the well-established KISS principle, but hey … we’re in lockdown and it’s good to have something to occupy the little grey cells.

One thing I soon discovered is that if you need a dropdown menu, you’ll be relying on the so-called “CSS Checkbox Hack”. The term “hack” isn’t quite fair, because the HTML and CSS used are perfectly legitimate and have valid use cases of their own. It’s just that there’s a no-doubt unintended way to use this particular feature of CSS.

There are scores of explanations of the checkbox hack on the web, so I’ll be brief. The idea is that you set up your HTML like this:

<label for="menu-toggle" class="menu-toggle-label">Menu</label>
<input type="checkbox" id="menu-toggle" class="menu-toggle">
<ul class="menu">
    <li>Blah</li>
</ul>

The label will be visible. The checkbox has to be hidden because it’s only there to trigger the action we need (and that’s the “hack”). The <ul> will initially be invisible.

Now, in HTML, if you click the label for a checkbox, it will toggle the checkbox’s “checked” state. And we can use that as a selector in CSS to drive a change in another element – for example to change the visibility of that <ul>. Here’s the relevant part of the CSS:

.menu, .menu-toggle {
    display: none;
}
.menu-toggle:checked + ul {
    display: block;
}

That second rule says that when menu-toggle is checked, find an adjacent <ul> and make it visible. Simple enough. So using the checkbox’s label effectively as a button causes the <ul> to appear and disappear.

The next challenge was to find a way to arrange WP’s menu HTML with the necessary labels and checkboxes. I think the only way to do this is to generate the HTML yourself, and the usual suggestion is to create an extension to the Walker_Nav_Menu class, where you can set up your own start/end HTML for the items and levels in the menu.

But that seemed like a tad too much complexity, so I went for a custom approach based on WP’s wp_get_nav_menu_items function. This brings back a sorted list of the menu items, each with a value defining its parent item, so you can work out what the submenu hierarchy is. By the way, I’d not realised until using this that the menu items are saved as posts in WP’s database.

The first bit of code here is where we grab the menu items and create an array of objects that reflect the hierarchy.

$menu_items = wp_get_nav_menu_items('primary-menu');
// Construct menu hierarchy
$menu = [];
foreach ($menu_items as $index=>$menu_item) {
	$parent = $menu_item->menu_item_parent;
	$menu_obj = new stdClass();
	$menu_obj->url = $menu_item->url;
	$menu_obj->title = $menu_item->title;
	if ($parent) { // Add submenu to parent object
		$menu[$lvl_index]->submenus[] = $menu_obj;		
	} else { // Make new parent object
		$menu[$index] = $menu_obj;
		$menu[$index]->submenus = [];
		$lvl_index = $index;
	}
}

We end up with an array of objects for the top-level menu items, each with an array of its submenu objects. Armed with this, we can generate the HTML. In my case, it looked like this. It’s a pretty straightforward iteration through the array and sub-menu arrays, shoving those labels and checkboxes in where necessary.

echo '<nav class="nav-primary">';
echo '<label for="menu-toggle" class="menu-toggle-label top-menu-toggle-label">Menu</label>';
echo '<input type="checkbox" id="menu-toggle" class="menu-toggle">';
echo '<ul class="menu menu-primary">';
foreach ($menu as $index=>$menu_item) {
	$has_submenu = count($menu_item->submenus);
	$classes = 'menu-item '.'menu-item'.$index;
	if ($has_submenu) $classes .= ' menu-item-has-children';
	echo '<li class="',$classes,'">';
	echo '<a class="menu-link" href="',$menu_item->url,'">';
	echo '<span>',$menu_item->title,'</span>';
	echo '</a>';
	if ($has_submenu) {
		echo '<label for="menu-toggle-',$index,'" class="menu-toggle-label">+</label>';
		echo '<input type="checkbox" id="menu-toggle-',$index,'" class="menu-toggle">';
		echo '<ul class="sub-menu">';
		foreach ($menu_item->submenus as $submenu_item) {
			echo '<li class="menu-item submenu-item">';
			echo '<a class="menu-link submenu-link" href="',$submenu_item->url,'">';
			echo '<span>',$submenu_item->title,'</span>';
			echo '</a>';
			echo '</li>';
		}
		echo '</ul>';
	}
	echo '</li>';
}
echo '</ul>';
echo '</nav>';

And hey, it works. But I decided against using it.

A few reasons for that.

First – it uses an unintended consequence of a CSS feature, and it puts checkboxes into a page that no user will ever see. That’s not what checkboxes are for.

Second – I still hold to the principle that CSS is for styling and JS is for functionality. This approach blurs that line.

Third – a JS approach allows me to do more with the HTML, like adding classes to dropdowns when they are visible … I can’t see a way to do this with CSS alone.

So all-in-all, a good exercise with some useful lessons.

Filed under: CSS, PHP, WordPress

PHP Calendar class

Posted on May 10, 2020

Note – I corrected this code in July 2021, because the original was unaware that some months span six Monday-to-Sunday weeks. The setDates() function is the one that needed to be corrected.

Further note – I decided to use PHP’s relative date calculations to simplify the code – once again, the setDates() function has been updated below.

I needed to build a monthly events calendar to display on one of my WP sites. I’d been using The Events Calendar plugin, which had served me well, but a recent new version had caused some issues, so I started looking at alternatives. To be fair, even with its problems, The Events Calendar was still better than the others for what I needed.

But the plugin was a heavyweight solution for my requirement, so I set about seeing if I could develop something of my own. One of the primary challenges was to generate a monthly calendar, ideally one with responsive markup.

There were many examples on the web, which were interesting enough, but most of them used HTML tables, so I tried a slightly different approach, and I’m happy with the solution I devised.

See a working example

Here’s the PHP class for the calendar:

class myCalendar {

private $curr_date, $prev_date, $next_date;
private $first_cal_day, $last_cal_day, $num_weeks, $has_events;

public function __construct($year, $month) {
    $this->setDates($year, $month);
}

public function setDates($year, $month) {

    $this->curr_date = new DateTime();
    $this->curr_date->setDate($year, $month, 1);

    // Last day of previous month
    $this->prev_date = new DateTime();
    $this->prev_date->setDate($year, $month, 0);

    // First day of next month
    $this->next_date = new DateTime();
    $this->next_date->setDate($year, $month + 1, 1);

    // Get first Monday to appear on calendar
    $this->first_cal_day = new DateTime();
    $this->first_cal_day->setDate($year, $month, 2)->modify('previous Monday');

    // Get last Sunday to appear on calendar
    $this->last_cal_day = new DateTime();
    $this->last_cal_day->setDate($year, $month + 1, -1)->modify('first Sunday');

    // Calculate number of weeks
    $diff = $this->first_cal_day->diff($this->last_cal_day);
    $this->num_weeks = ceil ($diff->days / 7);
}

public function get_date() {
    return $this->curr_date;
}
public function get_prevdate() {
    return $this->prev_date;
}
public function get_nextdate() {
    return $this->next_date;
}

public function day_names($format = 'abbr') {
    switch ($format) {
        case 'abbr':
            return ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];
            break;
        case 'full':
            return ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'];
            break;
        default:
            return ['M','T','W','T','F','S','S'];
            break;
    }
}

public function getCalendar($with_events = true) {

    if ($with_events) {
        $events = $this->getEvents();
    } else {
        $events = [];
    }
    $calendar = [];

    $oneday = new DateInterval('P1D');
    $calday = $this->first_cal_day;

    for ($w = 1; $w <= $this->num_weeks; $w++) {
        for ($d = 1; $d < 8; $d++) {
            $day = new stdClass();
            $event_date = $calday->format("Y-m-d");
            $day->date = clone $calday;
            $day->has_event = array_key_exists($event_date, $events);
            if ($day->has_event) {
                $day->events = $events[$event_date];
                $this->has_events = true;
            } else {
                $day->events = [];
            }
            $calendar[] = $day;
            $calday->add($oneday);
        }
    }
    
    return $calendar;

}

public function getEvents() {

    $events = [];

    $events['2020-04-01'][] = ['text' => 'Event1', 'slug' => 'event-one'];
    $events['2020-04-01'][] = ['text' => 'Event2', 'slug' => 'event-two'];
    $events['2020-04-05'][] = ['text' => 'Event3', 'slug' => 'event-three'];
    $events['2020-04-22'][] = ['text' => 'Event4', 'slug' => 'event-four'];

    $events['2020-05-01'][] = ['text' => 'Event5', 'slug' => 'event-five'];
    $events['2020-05-11'][] = ['text' => 'Event6', 'slug' => 'event-six'];
    $events['2020-05-25'][] = ['text' => 'Event7', 'slug' => 'event-seven'];
    $events['2020-05-27'][] = ['text' => 'Event8', 'slug' => 'event-eight'];   

    return $events;
    
}

public function has_events() {
    return $this->has_events;
}

}

After instantiating the object with a year and month, I can ask it for an array of days, covering the full weeks that span the start and end of the month. For example, the calendar for April 2020 runs from Monday March 30 to Sunday May 3.

Each element in the array is an object, with properties:
date = PHP DateTime object for the day
has_event = true / false showing whether there are events on this day
events = array of events occurring on this day

In my site, the events are a custom post type that are queried in the getEvents method, so I’ve hard-coded some examples in the code above. The events are presented as an array of arrays so that each date can have more than one event. I use the text property to display in the calendar, and the slug property becomes a link to the page for that specific event.

There are a few helper methods such as get_prevdate and get_nextdate, which can be used to create Previous / Next month links above the calendar on the web page.

Now, for presentation, rather than using HTML tables, I’m using “display: grid” for the calendar’s container div. For a wide screen, the CSS is:

.calendar-grid {
  display: grid;
  grid-template-columns: 14% 14% 14% 14% 14% 14% 14%;  
}

Then with a media query for smaller displays, it falls back to a simple vertical list. Neat, and with some accompanying use of display: none; I can easily hide any days with no events when it’s in this format.

@media screen and (max-width: 600px) {

.calendar-grid {
  grid-template-columns: auto;  
}
.no-event {
    display: none;
}
...

Here’s an example of using the class on the front end:

$calendar = new myCalendar($year, $month);
    
echo '<div class="calendar-container">';
    
echo '<div class="calendar-headers">';
echo '<div class="calendar-nav-prev"><a class="prev-month" href="?caldate=',$calendar->get_prevdate()->format('Y-m'),'"><< ',$calendar->get_prevdate()->format('F'),'</a></div>';
echo '<div class="calendar-title">',$calendar->get_date()->format('F Y'),'</div>';
echo '<div class="calendar-nav-next"><a class="next-month" href="?caldate=',$calendar->get_nextdate()->format('Y-m'),'">',$calendar->get_nextdate()->format('F'),' >></a></div>';
echo '</div>';

$caldays = $calendar->getCalendar();

if (!$calendar->has_events()) {
    echo '<div class="calendar-empty">No events scheduled for this month</div>';
}
    
echo '<div class="calendar-grid">';
     
foreach ($calendar->day_names() as $dayname) {
    echo '<div class="day-name">',$dayname,'</div>';
}
    
foreach ($caldays as $calday) {
    $calmonth = $calday->date->format("m");
    $cellclass = ($calmonth == $month) ? 'current-month' : 'adjacent-month';
    $cellclass .= $calday->has_event ? " has-event" : " no-event";
    echo '<div class="',$cellclass,'">';
    echo '<div class="dayinfull">',$calday->date->format("l j M"),'</div>';
    echo '<div class="daynumber">',$calday->date->format("d"),'</div>';
    if ($calday->has_event) {
      foreach ($calday->events as $day_event) {
        echo '<div class="calendar-event">';
        echo '<a href="/events/',$day_event['slug'],'">',$day_event['text'],'</a>';
        echo '</div>';
      }
    }
    echo '</div>';
}

// End the grid
echo '</div>';
// End the container
echo '</div>';

Filed under: PHP, WordPress

PHP echo performance revisited

Posted on May 8, 2020

PHP’s echo statement can be used with commas separating the elements to be output, or with dots. So either:

echo $a, $b, $c;
/* or */
echo $a . $b . $c;

I’ve usually preferred to use commas, because I figured that using a dot would cause PHP first to concatenate the items into a single string, and then output that. Seems like it would take longer, you’d think.

I’ve just seen a post at https://electrictoolbox.com/php-echo-commas-vs-concatenation/, apparently showing that I was right, and that using dots is slower than commas.

Thing is, this was posted many years ago, and I’m not sure what version of PHP the author was using.

I’ve just repeated the tests using the example code in that post, and on my local server running PHP 7.3, the results are different, and if anything, using dots is now quicker than using commas.

Using dots – elapsed time for the test was generally around 2.1 seconds

Using commas – elapsed time was pretty much always 2.3 seconds.

Now, for four million echos, that’s a negligible difference. I think I’ll stick with commas.

Filed under: PHP

Ternary and null coalescing operators

Posted on May 6, 2020

OK, I’m a bit late to the party on this, but it’s worth saying that the new null coalescing feature of PHP 7 has already saved a lot of mind-numbing code.

The scenario is all-too familiar. You are processing a form submission with some fields that don’t always turn up in $_POST, like unchecked checkboxes. That, or maybe you have to deal with query strings that may or may not be supplied, so one of the $_GET array elements might not be present.

In days gone by, ignorant even of the ternary operator, I would have done something like this:

$name = 'default';
if (isset($_GET['name'])) $name = $_GET['name'];
echo "Name is set to $name";

By the way, that snippet also shows how I usually try to avoid if .. else, by setting the default first. It’s just a personal thing.

Then one fine day I became aware of the ternary operator. Everything could be done on one line! Still annoying to have to type $_GET twice, but still.

$name = isset($_GET['name']) ? $_GET['name'] : 'default';
echo "Name is set to $name";

And now, PHP 7 has brought the mysteriously-named null coalescing operator, which shrinks the code even more.

$name = $_GET['name'] ?? 'default';
echo "Name is set to $name";

So, assign the $_GET array element, but if that isn’t set, use the default supplied. Still legible, and certainly a time-saver. I’m using it all the time now, and even changing old code to use this structure.

By the way, PHP 7.4 introduces the even-more-mysteriously-named Null coalescing assignment operator, using the ??= symbol. As its name suggests, it combines the condition and the assignment. For me, the downside is that the condition is applied to the same variable that will receive the assignment. This means that you’d end up doing this:

$_GET['name'] ??= 'default';
$name = $_GET['name'];
echo "Name is set to $name";

Well, another pet hate of mine is modifying $_GET and $_POST, so I’ll not be using this last structure, at least, not in the scenario I’ve been discussing.

Filed under: PHP

Primary Sidebar

Recent Posts

  • Populating Alpine data in a PHP page (part 2)
  • Thoughts on Tailwind CSS
  • Understanding JavaScript assignments
  • Populating Alpine data in a PHP page (part 1)
  • Alpine JS

Copyright Martin Taylor © 2025