Reading external JSON

My current site build involves an events calendar powered by jQuery, Google Calendar and Kirby articles filed into an events section. Theses all get plotted nicely on a visual calendar on the page that can flicked through, a bit like a Datepicker. Clicking on the events either takes you off to the event page within the Kirby site or opens a new tab with the google event in it. This is all working great except for one thing…

Along side the calendar is a chronological list of upcoming events grouped under month headings. This working great for the Kirby articles but i really need to get the Google Calendar events listed as well. This is where i’m lost. I think it means reading JSON from Googles API or doing some funky stuff to the Calendars .ICS feed that is meant to be read by programs like iCal or Microsoft office.

I know that Kirby can output data in JSON but how do i read/manipulte it? Has anybody done this before?

Any advice gratefully received. :slight_smile:

Just like you can encode JSON, you can also decode it (json_decode()), so you get a nice object or array that you can then further manipulate.

Edit: Google documentation how to consume the API via PHP

Thanks… i’ve seen that guide and others similar. Ive added the api client library supplied by google but it throws an error when i add it to a template.

I followed this guide: https://murze.be/2016/05/how-to-setup-and-use-the-google-calendar-api/

But it throws an error straight away at this line $client = new Google_Client();

Hmmm.

Do you mind telling us what sort of error you get :face_with_raised_eyebrow:?

Ive got past that error. Seems the library doesnt like not being installed by composer… now Kirby errors saying:

Call to undefined method Google_Client::loadServiceAccountJson()

Which im thinking is becuase theres no value to $credentialJson - that guide author was a bit vague on that.

Ah ha! seems the API has changed. sigh… this is well above my head.

Ok progress… after a bit of swearing, this gets data as JSON…

  <?php
  require_once 'vendor/autoload.php';
  $client = new Google_Client();
  $client->setAuthConfigFile('YOURCREDENTIALS.json'); // Replace this with your JSON file
  $client->addScope(Google_Service_Calendar::CALENDAR_READONLY);
  $calendarId = 'YOURID@group.calendar.google.com';
  $service = new Google_Service_Calendar($client);

  // $calendarId should contain the calendar id found on the settings page of the calendar
  $events = $service->events->listEvents($calendarId);

  // lets see what we get...
  var_dump($events);
  ?>

Now I guess it’s back to @texnixe first answer… :slight_smile:

Yeah, never use an old tutorial when it comes to using APIs :roll_eyes:

Seems a few months is a long time in the world of APIs… :frowning:

Anywho, now i’m stuck again. Seems it did not need decoding, the API took care of that so now i have a variable with data in it, similar to what was on that PHP documentation page you linked me to.

How do i pull bits of it out and display it? Apologies if im asking stupid questions but i only started learn PHP last week.

If you have a PHP array, you can loop through it with a foreach loop

<?php
foreach($events as $event) {
  echo $event; // or if the event is an array, get an item by key
}

(don’t know what your array looks like)

Well its pretty big but it looks like this…ive anonymised some of the values

  object(Google_Service_Calendar_Events)#192 (19) {
  ["collection_key":protected]=>
  string(5) "items"
  ["accessRole"]=>
  string(6) "reader"
  ["defaultRemindersType":protected]=>
  string(37) "Google_Service_Calendar_EventReminder"
  ["defaultRemindersDataType":protected]=>
  string(5) "array"
  ["description"]=>
  string(24) "ANONYMOUS"
  ["etag"]=>
  string(18) ""p320dhkk6qidda0g""
  ["itemsType":protected]=>
  string(29) "Google_Service_Calendar_Event"
  ["itemsDataType":protected]=>
  string(5) "array"
  ["kind"]=>
  string(15) "calendar#events"
  ["nextPageToken"]=>
  NULL
  ["nextSyncToken"]=>
  string(28) "CIDY0obUmtUCEIDY0obUmtUCGAE="
  ["summary"]=>
  string(10) "ANOYMOUS"
  ["timeZone"]=>
  string(19) "America/Los_Angeles"
  ["updated"]=>
  string(24) "2017-07-21T15:09:36.000Z"
  ["internal_gapi_mappings":protected]=>
  array(0) {
  }
  ["modelData":protected]=>
  array(0) {
  }
  ["processed":protected]=>
  array(0) {
  }
  ["defaultReminders"]=>
  array(0) {
  }
  ["items"]=>
  array(1) {
  [0]=>
  object(Google_Service_Calendar_Event)#201 (57) {
   ["collection_key":protected]=>
   string(10) "recurrence"
   ["anyoneCanAddSelf"]=>
   NULL
   ["attachmentsType":protected]=>
   string(39) "Google_Service_Calendar_EventAttachment"
   ["attachmentsDataType":protected]=>
   string(5) "array"
   ["attendeesType":protected]=>
   string(37) "Google_Service_Calendar_EventAttendee"
   ["attendeesDataType":protected]=>
   string(5) "array"
   ["attendeesOmitted"]=>
   NULL
   ["colorId"]=>
   NULL
   ["created"]=>
   string(24) "2017-07-19T11:00:19.000Z"
   ["creatorType":protected]=>
   string(36) "Google_Service_Calendar_EventCreator"
   ["creatorDataType":protected]=>
   string(0) ""
   ["description"]=>
   NULL
   ["endType":protected]=>
   string(37) "Google_Service_Calendar_EventDateTime"
   ["endDataType":protected]=>
   string(0) ""
   ["endTimeUnspecified"]=>
   NULL
   ["etag"]=>
   string(18) ""3000925632264000""
   ["extendedPropertiesType":protected]=>
   string(47) "Google_Service_Calendar_EventExtendedProperties"
   ["extendedPropertiesDataType":protected]=>
   string(0) ""
   ["gadgetType":protected]=>
   string(35) "Google_Service_Calendar_EventGadget"
   ["gadgetDataType":protected]=>
   string(0) ""
   ["guestsCanInviteOthers"]=>
   NULL
   ["guestsCanModify"]=>
   NULL
   ["guestsCanSeeOtherGuests"]=>
   NULL
   ["hangoutLink"]=>
   NULL
   ["htmlLink"]=>
   string(116) "ANONYMOUS"
   ["iCalUID"]=>
   string(37) "ANONYMOUS"
   ["id"]=>
   string(26) "ANONYMOUS"
   ["kind"]=>
   string(14) "calendar#event"
   ["location"]=>
   string(40) "ANONYMOUS"
   ["locked"]=>
   NULL
   ["organizerType":protected]=>
   string(38) "Google_Service_Calendar_EventOrganizer"
   ["organizerDataType":protected]=>
   string(0) ""
   ["originalStartTimeType":protected]=>
   string(37) "Google_Service_Calendar_EventDateTime"
   ["originalStartTimeDataType":protected]=>
   string(0) ""
   ["privateCopy"]=>
   NULL
   ["recurrence"]=>
   NULL
   ["recurringEventId"]=>
   NULL
   ["remindersType":protected]=>
   string(38) "Google_Service_Calendar_EventReminders"
   ["remindersDataType":protected]=>
   string(0) ""
   ["sequence"]=>
   int(1)
   ["sourceType":protected]=>
   string(35) "Google_Service_Calendar_EventSource"
   ["sourceDataType":protected]=>
   string(0) ""
   ["startType":protected]=>
   string(37) "Google_Service_Calendar_EventDateTime"
   ["startDataType":protected]=>
   string(0) ""
   ["status"]=>
   string(9) "confirmed"
   ["summary"]=>
   string(17) "Google Event"
   ["transparency"]=>
   string(11) "transparent"
   ["updated"]=>
   string(24) "2017-07-19T11:13:36.132Z"
   ["visibility"]=>
   NULL
   ["internal_gapi_mappings":protected]=>
   array(0) {
   }
   ["modelData":protected]=>
   array(0) {
   }
   ["processed":protected]=>
   array(0) {
   }
   ["creator"]=>
   object(Google_Service_Calendar_EventCreator)#191 (7) {
     ["displayName"]=>
     string(11) "ANONYMOUS"
     ["email"]=>
     string(31) "ANONYMOUS"
     ["id"]=>
     NULL
     ["self"]=>
     NULL
     ["internal_gapi_mappings":protected]=>
     array(0) {
     }
     ["modelData":protected]=>
     array(0) {
     }
     ["processed":protected]=>
     array(0) {
     }
   }
   ["organizer"]=>
   object(Google_Service_Calendar_EventOrganizer)#204 (7) {
     ["displayName"]=>
     string(10) "ANONYMOUS"
     ["email"]=>
     string(52) "ANONYMOUS"
     ["id"]=>
     NULL
     ["self"]=>
     bool(true)
     ["internal_gapi_mappings":protected]=>
     array(0) {
     }
     ["modelData":protected]=>
     array(0) {
     }
     ["processed":protected]=>
     array(0) {
     }
   }
   ["start"]=>
   object(Google_Service_Calendar_EventDateTime)#193 (6) {
     ["date"]=>
     string(10) "2017-07-20"
     ["dateTime"]=>
     NULL
     ["timeZone"]=>
     NULL
     ["internal_gapi_mappings":protected]=>
     array(0) {
     }
     ["modelData":protected]=>
     array(0) {
     }
     ["processed":protected]=>
     array(0) {
     }
   }
   ["end"]=>
   object(Google_Service_Calendar_EventDateTime)#205 (6) {
     ["date"]=>
     string(10) "2017-07-21"
     ["dateTime"]=>
     NULL
     ["timeZone"]=>
     NULL
     ["internal_gapi_mappings":protected]=>
     array(0) {
     }
     ["modelData":protected]=>
     array(0) {
     }
     ["processed":protected]=>
     array(0) {
     }
   }
   ["reminders"]=>
   object(Google_Service_Calendar_EventReminders)#198 (7) {
     ["collection_key":protected]=>
     string(9) "overrides"
     ["overridesType":protected]=>
     string(37) "Google_Service_Calendar_EventReminder"
     ["overridesDataType":protected]=>
     string(5) "array"
     ["useDefault"]=>
     bool(true)
     ["internal_gapi_mappings":protected]=>
     array(0) {
     }
     ["modelData":protected]=>
     array(0) {
     }
     ["processed":protected]=>
     array(0) {
     }
   }
  }
  }
  }

So basically i need to merge data from that array with my side bar nav that currently contains kirby articles and have the google calendar events fall into the list in chronological order as if they were articles in Kirby:

    <?php
    // loop through the years
    $months = $site->find("events")->children()->visible()->filterBy('startdate', '>', date('Y-m-d'))->groupBy('startdate');

    foreach($months as $month => $itemsPerMonth): ?>
        <?php $eventdate = $month; ?>
        <?php $eventmonth = date("F",strtotime($eventdate)); ?>
        <?php $eventday = date("j",strtotime($eventdate)); ?>

        <h4><?php echo $eventmonth ?></h4>

        <ul class="article-subnav">
          <?php foreach($itemsPerMonth as $item) : ?>
          <li><a href="<?php echo $item->url() ?>"><strong class="day"><?php echo $eventday ?></strong>
           <?php echo $item->title() ?></a>
          </li>
          <?php endforeach; ?>
        </ul>
    <?php endforeach ?>

Well, currently, you don’t have an array, but an object of type Google_Service_Calendar_Events. The documentation tells you, how and what you can get from it (for example, getItems()). Then dump the items to see what’s in there and so on.

Unfortunately, this is far beyond Kirby support. If you want to merge that with Kirby stuff, I think the best way is to try to get the information you need out of the API, then merge Kirby events and Google API events into some sort of array.

Sure, I appreciate it’s a bit out of Kirby support but thanks for the nudge. I asked originally because i thought there was probably some way built into to Kirby for reading the data.

Each API is different and like Kirby, each object has its methods to extract the data you need. The principle is always the same, like Kirby’s page or collection objects have their methods to manipulate the objects.

sadly i do not know about the google api.

wasn’t part of your question but if you ever need to create ics yourself i found this lib useful - if you set timezone correctly that is.

So im at the last hurdle. I now have an array with the merged events, but when i try to use filterBy to sort it by date order, i get an error:

My array looks like:

    array(3) {
    [0]=>
    array(4) {
    ["ETitle"]=>
    object(Field)#231 (3) {
    ["page"]=>
    string(19) "events/launch-party"
    ["key"]=>
    string(5) "title"
    ["value"]=>
    string(12) "Launch Party"
    }
    ["EStart"]=>
    string(19) "2017-07-26T09:00:00"
    ["EEnd"]=>
    string(19) "2017-04-28T17:00:00"
    ["ELink"]=>
    string(35) "http://example.dev/events/launch-party"
    }
    [1]=>
    array(4) {
    ["ETitle"]=>
    object(Field)#248 (3) {
    ["page"]=>
    string(15) "events/open-day"
    ["key"]=>
    string(5) "title"
    ["value"]=>
    string(8) "Open Day"
    }
    ["EStart"]=>
    string(19) "2017-08-10T00:00:00"
    ["EEnd"]=>
    string(19) "2017-08-11T00:00:00"
    ["ELink"]=>
    string(31) "http://example.dev/events/open-day"
    }
    [2]=>
    array(4) {
    ["ETitle"]=>
    string(17) "Test Google Event"
    ["EStart"]=>
    NULL
    ["EEnd"]=>
    NULL
    ["ELink"]=>
    string(116) "https://www.google.com/calendar/event?eid=NjdsZW9ydmE4bWlmOHRnZ3J0dGw3MTVlamIgcnY3YTZyMTlmMjQyMHZvcmFkcWNrbW1zdG9AZw"
    }
    }

But when i try to order it with:

  $allevents = array_merge($kcaleventlist, $gcaleventlist);
  $chronos = $allevents->filterBy('EStart', '>', date('Y-m-d'))->groupBy('EStart');
  var_dump($chronos);

I get the following message:

Call to a member function filterBy() on array

Do i need to turn the array into a collection or something? How would i do that?

Well, object methods like filterBy() (which is a method of the Kirby Collection class and the classes that inherit from it) can’t be used with arrays.

You can use PHP array filtering functions instead, for example http://php.net/manual/en/function.array-filter.php

That manual is your friend for all things PHP, anyway. So better bookmark it :wink:

Thanks :slight_smile: Ill bookmark the english version. Cant cope with learning German at the same time as PHP :slight_smile:

I’ve just noticed that the array has multi dimensions for the kirby articles. How do i flatten it out so that ive literally just got the page title for those? I’m creating the array of Kirby articles like this:

  $kirbyevents = $site->find("events")->children()->visible();

  $kcaleventlist = array ();

    foreach ($kirbyevents as $kcalendarData) {

    $kstartdate = $kcalendarData->startdate();
    $kstarttime = $kcalendarData->starttime();
    $kenddate = $kcalendarData->enddate();
    $kendtime = $kcalendarData->endtime();

    $kstartisoDate = date('Y-m-d\TH:i:s', strtotime("$kstartdate $kstarttime"));
    $kendisoDate = date('Y-m-d\TH:i:s', strtotime("$kenddate $kendtime"));

    $ktitle = $kcalendarData->title();

    $kcaleventlist[] = array('ETitle' => $ktitle, 'EStart' => $kstartisoDate, 'EEnd' => $kendisoDate, 'ELink' => $kcalendarData->url());

  } 

The result is this:

    [0]=>
    array(4) {
      ["ETitle"]=>
      object(Field)#231 (3) {
        ["page"]=>
        string(19) "events/launch-party"
        ["key"]=>
        string(5) "title"
        ["value"]=>
        string(12) "Launch Party"
      }
      ["EStart"]=>
      string(19) "2017-07-26T09:00:00"
      ["EEnd"]=>
      string(19) "2017-04-28T17:00:00"
      ["ELink"]=>
      string(35) "http://dama.dev/events/launch-party"
    }
    [1]=>
    array(4) {
      ["ETitle"]=>
      object(Field)#248 (3) {
        ["page"]=>
        string(15) "events/open-day"
        ["key"]=>
        string(5) "title"
        ["value"]=>
        string(8) "Open Day"
      }
      ["EStart"]=>
      string(19) "2017-08-10T00:00:00"
      ["EEnd"]=>
      string(19) "2017-08-11T00:00:00"
      ["ELink"]=>
      string(31) "http://example.dev/events/open-day"
    }
    }

But i was expecting this:

    array(1) {
      [0]=>
      array(4) {
      ["ETitle"]=>
      string(17) "Launch Event"
      ["EStart"]=>
      '2017-04-28T17:00:00'
      ["EEnd"]=>
      '2017-04-28T17:00:00'
      ["ELink"]=>
      string(116) "http://example.dev/events/launch-party"
      }
    }

Instead of passing the field object, just pass the value:

 $ktitle = $kcalendarData->title()->value();