Skip to content

Commit

Permalink
feat: add option to specify fields in linked tables
Browse files Browse the repository at this point in the history
  • Loading branch information
marcantondahmen committed Jan 19, 2023
1 parent 05c4e89 commit b0b2bc4
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 32 deletions.
16 changes: 10 additions & 6 deletions docs/source/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ theme that supports Automad's block editor. The markup looks as follows:
fields: 'Name, Client',
filters: 'Client, Category',
formula: 'SEARCH(LOWER("@{ ?search }"), LOWER({Name}))',
linked: 'Client => Clients',
linked: 'Client => Clients[Name Address]',
prefix: ':example',
template: '
{{#records}}
Expand Down Expand Up @@ -77,11 +77,15 @@ Name Description
prefixes have to be unique, in case
more than one Airmad instance is used on the site
``linked`` A comma separated list of tables that are linked to a field
of the main table records --- note that is only required to list linked tables
here that include information that you want to display. In case the field name
differs from the actual table name to be linked, it is also possible to pass
a list of strings like ``fieldName1 => tableName1, fieldName2 => tableName2``
to the parameter to link such fields to any table.
of the main table records --- note that it is only required to list linked tables
here that include information that you want to display.
In case the field name differs from the actual table name to be linked,
it is also possible to pass a list of strings like
``field1 => table1, field2 => table2`` to the parameter to link such fields to
any table.
In order to improve performance, you can also specify the fields in the linked
table like ``field => table[field1 field2]``. This will reduce significantly
the amount of data requested.
``template`` The Handlebar template to be used to render the model
(the collection of records) ---
can be either a string, a variable containing a string or a file path
Expand Down
5 changes: 2 additions & 3 deletions src/API.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public function __construct($options) {
* @param string $table
* @param string $view
* @param string $formula
* @param string $fields
* @param array $fields
*/
public function getRecords($table, $view = false, $formula = false, $fields = array()) {
$cache = new Cache($this->options->base, $table, $view, $formula, $fields);
Expand All @@ -64,7 +64,7 @@ public function getRecords($table, $view = false, $formula = false, $fields = ar
$url = "$this->apiUrl/{$this->options->base}/$table";

$query = array(
'maxRecords' => 100000,
'maxRecords' => 50000,
'pageSize' => 100,
'view' => $view,
'filterByFormula' => $formula
Expand All @@ -84,7 +84,6 @@ public function getRecords($table, $view = false, $formula = false, $fields = ar
}

$queryString = http_build_query($query);

$data = $this->request("$url?$queryString");

if (isset($data->offset)) {
Expand Down
55 changes: 55 additions & 0 deletions src/Debug.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
/**
* Airmad
*
* An Airtable integration for Automad.
*
* @author Marc Anton Dahmen
* @copyright Copyright (C) 2023 Marc Anton Dahmen - <https://marcdahmen.de>
* @license MIT license
*/

namespace Airmad;

defined('AUTOMAD') or die('Direct access not permitted!');

class Debug {
/**
* The log entries array.
*/
private static $entries = array();

/**
* Get the log entries
*
* @return array
*/
public static function get(): array {
return self::$entries;
}

/**
* Add an entry to the entries array.
*
* @param mixed $entry
* @return void
*/
public static function log(mixed $entry): void {
$backtraceAll = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5);
$ignoreFunctions = array('log', __NAMESPACE__ . '\{closure}');

$backtrace = array_filter($backtraceAll, function ($item) use ($ignoreFunctions) {
return (isset($item['class'], $item['type'], $item['function']) && !in_array($item['function'], $ignoreFunctions));
});

if (count($backtrace) > 0) {
// When the backtrace array got reduced to the actually relevant items in the backtrace, take the first element (the one calling Debug::log()).
$backtrace = array_shift($backtrace);
$src = basename(str_replace('\\', '/', $backtrace['class'] ?? '')) . ($backtrace['type'] ?? '') . $backtrace['function'] . '(): ';
} else {
$src = basename($backtraceAll[0]['file']);
}

self::$entries[] = array($src, $entry);
}
}
49 changes: 26 additions & 23 deletions src/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
namespace Airmad;

use Automad\Core\Parse;
use Automad\Core\Request;

defined('AUTOMAD') or die('Direct access not permitted!');

Expand Down Expand Up @@ -61,6 +60,8 @@ class Model {
public function __construct($options, $Automad) {
$this->options = $options;

Debug::log($options);

$cache = new ModelCache($options);

if ($data = $cache->load()) {
Expand All @@ -87,14 +88,23 @@ public function __construct($options, $Automad) {
* @return array The model object.
*/
public function get() {
return (object) array(
'records' => $this->records,
'filters' => $this->filterData,
'filteredFilters' => $this->filteredFilterData,
'query' => $_GET,
'count' => count($this->records),
'pages' => ceil(count($this->records) / $this->options->limit),
'automad' => $this->AutomadDataBridge->get()
$data = array();

if (AM_DEBUG_ENABLED) {
$data['debug'] = Debug::get();
}

return (object) array_merge(
$data,
array(
'records' => $this->records,
'filters' => $this->filterData,
'filteredFilters' => $this->filteredFilterData,
'query' => $_GET,
'count' => count($this->records),
'pages' => ceil(count($this->records) / $this->options->limit),
'automad' => $this->AutomadDataBridge->get()
)
);
}

Expand All @@ -108,20 +118,13 @@ private function buildTableMap($links) {
$tableMap = array();

foreach ($links as $link) {
$parts = preg_split('/\s*\=\>\s*/', $link);

if (!empty($parts)) {
$field = $parts[0];
$table = $field;

if (!empty($parts[1])) {
$table = $parts[1];
}

$tableMap[$field] = $table;
if ($TableLink = TableLink::fromString($link)) {
$tableMap[$TableLink->field] = $TableLink->Table;
}
}

Debug::log($tableMap);

return $tableMap;
}

Expand Down Expand Up @@ -243,8 +246,8 @@ private function getTables() {
$this->options->fields
);

foreach (array_values($this->tableMap) as $tableName) {
$tables[$tableName] = $API->getRecords($tableName);
foreach (array_values($this->tableMap) as $table) {
$tables[$table->name] = $API->getRecords($table->name, false, false, $table->fields);
}

return $tables;
Expand All @@ -261,7 +264,7 @@ private function prepareRecords($tables) {
foreach ($record->fields as $fieldName => $ids) {
if (in_array($fieldName, array_keys($this->tableMap))) {
$linkedRecords = array();
$tableName = $this->tableMap[$fieldName];
$tableName = $this->tableMap[$fieldName]->name;

foreach ($ids as $id) {
$key = array_search($id, array_column($tables[$tableName], 'id'));
Expand Down
37 changes: 37 additions & 0 deletions src/Table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php
/**
* Airmad
*
* An Airtable integration for Automad.
*
* @author Marc Anton Dahmen
* @copyright Copyright (C) 2023 Marc Anton Dahmen - <https://marcdahmen.de>
* @license MIT license
*/

namespace Airmad;

defined('AUTOMAD') or die('Direct access not permitted!');

class Table {
/**
* A list of fields that have to be included in the request.
*/
public array $fields;

/**
* The table name to be requested.
*/
public string $name;

/**
* The constructor.
*
* @param string $name
* @param array $fields
*/
public function __construct(string $name, array $fields) {
$this->name = $name;
$this->fields = $fields;
}
}
79 changes: 79 additions & 0 deletions src/TableLink.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php
/**
* Airmad
*
* An Airtable integration for Automad.
*
* @author Marc Anton Dahmen
* @copyright Copyright (C) 2023 Marc Anton Dahmen - <https://marcdahmen.de>
* @license MIT license
*/

namespace Airmad;

defined('AUTOMAD') or die('Direct access not permitted!');

class TableLink {
/**
* A field that links to the table.
*/
public string $field;

/**
* The table name to be requested.
*/
public Table $Table;

/**
* The constructor.
*
* @param string $name
* @param Table $Table
*/
public function __construct(string $field, string $table, array $tableFields) {
$this->field = $field;
$this->Table = new Table($table, $tableFields);
}

/**
* Parse a table link string.
*
* Possible link formats are:
* Table
* Table[Field1 Field2]
* Field => Table
* Field => Table[Field1 Field2]
*
* @param string $link
* @return TableLink|null
*/
public static function fromString(string $link): ?TableLink {
$parts = preg_split('/\s*\=\>\s*/', $link);

if (empty($parts)) {
return null;
}

$field = trim(preg_replace('/\[[\w\s]+\]/is', '', $parts[0]));

if (empty($parts[1])) {
$tableAndFields = $parts[0];
} else {
$tableAndFields = $parts[1];
}

preg_match('/(?P<name>\w+)(?:\[(?P<fields>[\w\s]+)\])?/', $tableAndFields, $matches);

if (!empty($matches) && !empty($matches['name'])) {
$tableFields = array();

if (!empty($matches['fields'])) {
$tableFields = preg_split('/\s+/', $matches['fields']);
}

return new TableLink($field, $matches['name'], $tableFields);
}

return null;
}
}
1 change: 1 addition & 0 deletions src/Utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public static function loop($data, $context, $template) {

return $buffer;
}

/**
* Resolves the values for a given CSV list. Values can either be double quoted strings or field names.
* Field names are resolved than to a value while strings will just have their wrapping quotes removed.
Expand Down

0 comments on commit b0b2bc4

Please sign in to comment.