Get file path using Kirby Builder + Vue.js Starterkit

Hi all,

I’m struggling with some file path stuff using Kirby Builder and Vue.js Starterkit…

For the vue.js API stuff to work, I have to convert from php to json:

<?php
$data = [
  'url' => $page->url(),
  'title' => $page->title()->value(),
  'heroImage' => $page->heroImage()->toFile()->url(),
  'overview' => $page->overview()->value(),
  'builderBlocks' => $page->projectBlocks()->yaml()
];
echo json_encode($data);

And here is my .txt file generated from the blueprints:

Heroimage:

- hero-image-fpo.jpg

----

Projectblocks:

- 
  video:
    - example-video.mp4
  caption: ""
  _key: videoPlayerBlock
  _uid: videoPlayerBlock_1596324496754_332

Now, the Heroimage above works because I can add the helpers prior to the json encoding:
'heroImage' => $page->heroImage()->toFile()->url()

This returns the full image path, and I can see this in my Vue.js stuff perfectly:
http://127.0.0.1:8000/media/pages/work/project-01/bbc2e1e530-1595882660/hero-image-fpo.jpg

But the Projectblocks come through as one magic array from the yaml() function, so can’t use these helpers like file->toFile()->url() since this is all being converted to an array, so it just returns the path like this:
example-video.mp4

How can I get this to output similar to the hero image example above?
http://127.0.0.1:8000/media/pages/work/project-01/bbc2e1e530-1595882660/example-video.mp4

(I know the bbc2e1e530-1595882660 is a unique folder generated per each file upload, so these would be different, this is all just for example purposes…)

Thank you all for your help!!

I feel like I could do some PHP before converting the builder blocks to json…

I’m awful with PHP, but what about something like this:

<?php

$projectBlocks = $page->projectBlocks()->yaml();
foreach ($projectBlocks as $key => $block):
  if ($block['_key'] == 'videoPlayerBlock') {
    $v = $block['video'][0];
    // this breaks, but the idea is to find the video key and replace it with a helper...
    // $projectBlocks[$key]['video'] = "/media/pages/work/project-01/bbc2e1.../example-video.mp4";
    $projectBlocks[$key]['video'] = $v->toFile()->url();
  }
endforeach;

$data = [
  'url' => $page->url(),
  'title' => $page->title()->value(),
  'heroImage' => $page->heroImage()->toFile()->url(),
  'overview' => $page->overview()->value(),
  'builderBlocks' => $projectBlocks
];

echo json_encode($data);

The idea here is to store our $projectBlocks as yaml - which has everything else I need, just loop through it all and modify / update specific values, such as this video key to use a url helper?

You can use array_map(). Note that this only works for single files and you would have to adapt the code for multiple files in a block or build the array manually if you have more cases that don’t work with simply converting to an array (image galleries etc.)

$builderblocks = $page->projectBlocks()->yaml();

$builderblocks = array_map( function($block) use($page){

 if($block['_key'] === 'videoPlayerBlock') {
    $block['video'] = ( $file = $page->file($block['video'][0])) ? $file->url() : '';
    return $block;  
  }

}, $builderblocks);

You can also make it work with a foreach loop, but with some important changes

$builderblocks = $page->projectBlocks()->yaml();
foreach ($builderblocks as $key => &$block) {
  if ($block['_key'] === 'videoPlayerBlock') {
    $block['video'] = ( $file = $page->file($block['video'][0])) ? $file->url() : '';
  }
}
  1. To be able to change the variable, you have to pass it by reference (using &$block instead of $block)
  2. You cannot call an object method (toFile()) on a string and $v in you example is a simple string, not a field object and toFile() is a method of the Field class.
  3. $v->toFile()->url(): You call the url() File method without prior check if you have a File object, so even if calling toFile() on $v was possible, you would have to make sure the result of this call returns an object before you call url().

A bit much if you are not familiar with Object Oriented Programming, I know…

1 Like

Hey Sonja, thank you for these examples and detailed explanations! :zap:

I tried both examples above and keep getting an error though - I’m unfamiliar with the dump($builderblocks); at the end of each loop - I’m still passing my data in like so:

<?php

$builderblocks = $page->projectBlocks()->yaml();
foreach ($builderblocks as $key => &$block) {
  if ($block['_key'] === 'videoPlayerBlock') {
    $block['video'] = ($file = $page->file($block['video'][0])) ? $file->url() : '';
  }
}

$data = [
  'url' => $page->url(),
  'title' => $page->title()->value(),
  'heroImage' => $page->heroImage()->toFile()->url(),
  'overview' => $page->overview()->value(),
  'builderBlocks' => $builderblocks
];

echo json_encode($data);

Anything jump out that would make this break? :thinking:

The dump($builderblocks) is only for (simple) debugging purposes in PHP to check if you get the desired results, it should not be used when you call the route in Vue.js, so in other words: remove that line.

Cool, hah. No more error, however, when I log / reference my page.builderBlocks in Vue.js, the value comes back as original example-video.mp4 path - the following line isn’t updating at all:
$block['video'] = ($file = $page->file($block['video'][0])) ? $file->url() : '';

Try using the array_map approach.

Trying that approach, but I’m getting an anonymous PHP error:

<?php

$builderblocks = $page->projectBlocks()->yaml();

$builderblocks = array_map(function($block) use($page) {
  if ($block['_key'] === 'videoPlayerBlock') {
    $block['video'] = ($file = $page->file($block['video'][0])) ? $file->url() : '';
    return $block;
  }
}, $builderblocks);

$data = [
  'url' => $page->url(),
  'title' => $page->title()->value(),
  'heroImage' => $page->heroImage()->toFile()->url(),
  'overview' => $page->overview()->value(),
  'builderBlocks' => $builderblocks
];

echo json_encode($data);

I think $builderblocks is passing through as null

What do you get when you open that page in the browser? And on which line in the code is the error thrown?

On a side note, you should also change your heroimage line:

'heroImage' => ( $file = $page->heroImage()->toFile() ) ? $file->url() : '',

As I already said, never call a class method without a prior check if your have an object of that class.

With the code I suggested I get the desired json, at least when I open my example in browser:

{
  "url": "http://34.test/projects/trees",
  "title": "Trees",
  "overview": null,
  "builderBlocks": [
    {
      "bild": "http://34.test/media/pages/projects/trees/753fb86f27-1593879878/cheesy-autumn.jpg",
      "caption": "",
      "_key": "videoPlayerBlock",
      "_uid": "videoPlayerBlock_1596324496754_332"
    },
    {
      "bild": "http://34.test/media/pages/projects/trees/dfa346c6d8-1593879878/last-tree-standing.jpg",
      "caption": "",
      "_key": "videoPlayerBlock",
      "_uid": "videoPlayerBlock_1596324496754_332"
    }
  ]
}

I named the field bild instead of video, but that doesn’t matter.

Nice, thanks for the reminder for that ‘heroimage’ :+1:

Because the Vue.js Starterkit uses npm run dev to fire up it’s own php and node servers, I have no idea how to see any php errors - anytime there are PHP errors, it just shows the blank error page. I turned debugging mode to true in Kirby.config but that didn’t do anything.

In my _.vue file I’m logging this.page on mounted()

{…}

  builderBlocks: Array(1)
    0: null
    length: 1
    __ob__: Observer {value: Array(1), dep: Dep, vmCount: 0}
    __proto__: Array
  heroImage: "http://127.0.0.1:8000/media/pages/work/project-01/bbc2e1e530-1595882660/hero-image-fpo.jpg"
  overview: "Lorem ipsum overview copy here."
  title: (...)
  url: (...)
  ...

So other parts come through fine.

That’s great it is working for you, haha, can you send your working function example so I can compare?

As I said, I only opened the json template in the browser in a modified Starterkit where I added some content to an album page:

<?php

$builderblocks = $page->projectBlocks()->yaml();
$builderblocks = array_map(function($block) use($page) {
  if ($block['_key'] === 'videoPlayerBlock') {
   $block['bild'] = ($file = $page->file($block['bild'][0])) ? $file->url() : '';
    return $block;
  }
}, $builderblocks);

$data = [
  'url' => $page->url(),
  'title' => $page->title()->value(),
  //'heroImage' => $page->heroImage()->toFile()->url(),
  'overview' => $page->overview()->value(),
  'builderBlocks' => $builderblocks
];

echo json_encode($data);

With this data in the content file (copied from what you had above but slightly modified to use images instead of a video file)

Projectblocks:

- 
  bild:
    - cheesy-autumn.jpg
  caption: ""
  _key: videoPlayerBlock
  _uid: videoPlayerBlock_1596324496754_332
- 
  bild:
    - last-tree-standing.jpg
  caption: ""
  _key: videoPlayerBlock
  _uid: videoPlayerBlock_1596324496754_332

DOH!!! Hahah :dizzy_face: :+1: I had a typo in my if statement:

if($block['_key'] === 'videoPlayerBlock') { ... }

Thank you for re-pasting your code above, it jumped out at me as soon as I saw your .txt example, so thank you. I’m embarrassed, but I just tested and this working great.

Marking your answer form earlier as the solution (we could remove the dump($builderblocks) line though).

Both array_map and foreach loop solutions worked great, I’m such an idiot :roll_eyes: :+1: