Create pages from .csv

You don’t need the site method, I think. You can use the “inner parts” of that function inside the original ` csv function.

this doesn’t seems to work

function csv(string $file, string $length, string $delimiter = ','): array {

  $fh = fopen($file, $length, $delimiter);
  $header_line = fgets($fh);
  $header_line = str_replace("\xEF\xBB\xBF", '', $header_line);
  $keys = str_getcsv($header_line);
  $csv = [];

  while ($row = fgetcsv($fh, $delimiter)) {
     $csv[] = array_combine($keys, $row);
  }

  return $csv;

}

Could you please indicate what doesn’t work? Do you get an error? Or no result?

yes i get an error message: fopen(/content/folder/file.csv): failed to open stream: Undefined error: 0
The path is correct. I added line breaks into the animals csv file for testing purpose.

Here is the rest of the code:

$csv      = csv($page->root() . '/file.csv', ',');
$kirby->impersonate('kirby');
foreach ($csv as $animal) {
  $newPage = page('animals')->createChild([
    'slug' => Str::slug($animal['title']),
    'template' => 'animal',
    'model'    => 'animal',
    'draft' => 0,
    'num' => 0,
    'content'  => [
      'id' => $animal['id'],
      'title' => $animal['title'],
      'text' => $animal['content']
  ]
  ])->changeStatus("listed", $i++);

};

I think this line is wrong an should be just

$fh = fopen($file, 'r');

Thanks a lot !

Is there is a way to download a picture into the created folder, from url contained in a specific image URL’s field of the csv?
I have a field into the csv named Image URL, with an image url as value.

So it could become a cover for the page?
thanks for the precious help

Yes, you could create a remote request to fetch the image, then save it into the page folder.

Hello @texnixe, I have quite a similar use case as the original question.

As a one time thing, I would like to create pages from a CSV programmatically. I have used the solution that you provided, however this line is throwing an “Undefined index: title” error:
'slug' => Str::slug($flag['title']),

Here is the code I have included in my flags.php template.

<?php 
// the `csv` function can live in a plugin file or in the template where you want to create the children if its only a one time thing
function csv(string $file, string $delimiter = ','): array
{
    $lines = file($file);

    $lines[0] = str_replace("\xEF\xBB\xBF", '', $lines[0]);

    $csv = array_map(function($d) use($delimiter) {
        return str_getcsv($d, $delimiter);
    }, $lines);

    array_walk($csv, function(&$a) use ($csv) {
       $a = array_combine($csv[0], $a);
    });

    array_shift($csv);

    return $csv;
}

// Adapt this to your csv file name and the fields in your csv file:
// Example based on the guide example
    $csv      = csv($page->root() . '/flags.csv', ';');
    $kirby->impersonate('kirby');
    foreach ($csv as $flag) {
      $newPage = page('flags')->createChild([
        'slug' => Str::slug($flag['title']),
        'template' => 'flag',
        'content'  => [
          'title'    => $flag['title'],
          'colours'  => $flag['colours'],
      ]

      ]);
    };  

 ?>

My CSV file definitely includes title, so I am not sure what could be causing this. Any help would be greatly appreciated :slight_smile:

Use dump() to see what $csv returns.

The contents of the dump was:

Array
(
    [0] => Array
        (
            [title,colours] => France,"blue, red"
        )

    [1] => Array
        (
            [title,colours] => Bahamas,yellow
        )

)

You are using the wrong delimiter. You columns are separated by comma, not semicolon.

1 Like

Thanks so much @texnixe, that solved it :raised_hands: