Iterate pages with CLI command


#1

This thread started in the discord dev channel with @OleVik.

It seems similar to this thread:


I want to loop through pages in a CLI command. How should I access my pages?

This doesn;t work:

        $grav = Grav::instance();
        $pages = $grav["pages"];
        if ($pages === null) {
            $this->output->writeln("No pages.");
            return;
        }

It doesn’t return null, but I can’t use $page = $pages->find("/", true); . It returns null
The $pages= doen’t return null, but the ->find() will return null

OleVik:

A normal foreach on $pages ?

For a concrete example, applicable to CLI: https://learn.getgrav.org/cookbook/plugin-recipes#iterating-through-pages-and-media

@ OleVik Thanks for the suggestion. I tried foreach (as per your suggestion) again, without result. This is the slimmed down code:

protected function serve()
{

    $grav = Grav::instance();
    $pages = $grav["pages"];
    if ($pages === null) {
        $this->output->writeln("No pages.");
        return;
    }
    // dump( $pages );
    dump ( 'Start foreach' );
    foreach ($pages as $page) {
        $this->output->writeln( $page->route() );
    }
    dump ( 'End foreach' );
}

The only thing you get to see is the output of the dump() .

Once I get this working, I actually (also) need to find a parent page (e.g. ‘/blog’) first, and look through its children or descendants. The main loop nor the find are working atm.

@ OleVik A variant (from the example in the link you provided) also does not make a difference:

protected function serve()
{
    $page = Grav::instance()['page'];
    $pages = $page->evaluate(['@page.self' => '/']);
    if ($pages === null) {
        $this->output->writeln("No pages.");
        return;
    }
    dump ( 'Start foreach' );
    foreach ($pages as $page) {
        $this->output->writeln( $page->rawRoute() );
    }
    dump ( 'End foreach' );
}

The $pages array appears to be empty in both cases?

OleVik:

Could you post the full PHP-file somewhere, or add it using the + button next to the textarea? I can’t immediately tell why.

@ OleVik I found a related forum post that suggests using $grav->process() after instance() , but that throws an error about a missing array.


#2

Full code of my CLI Command

ScanCommand.php

<?php
namespace Grav\Plugin\Console;

use Grav\Common\Grav;
use Grav\Common\Page\Page;
use Grav\Common\Page\Pages;
use Grav\Console\ConsoleCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;

/**
 * Class ScanCommand
 *
 * @package Grav\Plugin\Console
 */
class ScanCommand extends ConsoleCommand
{
    /**
     * @var array
     */
    protected $options = [];

    /**
     * Scans all pages for static maps from mapbox.com
     */
    protected function configure()
    {
        $this
            ->setName("scan")
            ->setDescription("Scan for maps")
            ->addArgument(
                'route',
                InputArgument::REQUIRED,
                'The starting route'
            )
            ->setHelp('The <info>scan</info> scans for all available static maps.')
        ;
    }

    /**
     * @return int|null|void
     */
    protected function serve()
    {

        // Collects the arguments and options as defined
        $this->options = [
            'route' => $this->input->getArgument('route'),
        ];

        $grav = Grav::instance();
        // $grav->process();
        $pages = $grav["pages"];
        // dump( $pages );

        // Test loop and return
        foreach ($pages as $page) {
            // Just output something for now
            $this->output->writeln( $page->rawRoute() );
        }
        return;

        $parent_route = $this->options['route'];
        // dump( $parent_route );
        $parent_page = $pages->find($parent_route, true);
        // dump( $parent_page) ;

        if ($parent_page === null) {
            $this->output->writeln("No page found with route: <cyan>'".$parent_route."'</cyan>");
            return;
        }

        $msg = "<red>Not implement yet.</red>";
        $this->output->writeln($msg);
    }
}


#3

You need to wait until Pages can be made available, then initialize them. Eg.:

/* Initialize Grav and Pages */
$grav = Grav::instance();
$grav->fireEvent('onPagesInitialized');
$grav['pages']->init();
$pages = $grav['pages']->all();
// Report how many Pages there are
dump(count($pages));

/* If we only want a subset of pages, let's evaluate a route */
$page = Grav::instance()['page'];
$pages = $page->evaluate([
    '@page.children' => $this->options['route']
]);
$pages = $pages->published()->order('date', 'desc');
$paths = array();
// Iterate through Pages, store some properties
foreach ($pages as $page) {
    $route = $page->rawRoute();
    $path = $page->path();
    $title = $page->title();
    $paths[$route] = $title;
}
// Report those properties
dump($paths);
exit();
return;

With some sample Pages this yields:

C:\Caddy\Test
λ php bin/plugin cli-test scan /book
9
array:6 [
  "/book/postscript" => "Postscript"
  "/book/the-queens-crocquet-ground" => "The Queen’s Croquet-Ground"
  "/book/a-mad-tea-party" => "A Mad Tea-Party"
  "/book/were-all-mad-here" => "We're all mad here"
  "/book/advice-from-a-caterpillar" => "Advice from a Caterpillar"
  "/book/down-the-rabbit-hole" => "Down the Rabbit-Hole"
]