Maps are used to convert one or more objects to another format. Maps can be used to export data, format objects for the REST API, or deposit data in third-party services like Crossref.
A Map can be written to prepare data in JSON, XML, CSV or any other format. The most common maps are those used to convert a DataObject to JSON according to its schema.
A map must implement at least one public method to perform the map.
namespace PKP\announcement\maps;
use PKP\announcement\Announcement;
use PKP\services\PKPSchemaService;
class Schema extends \PKP\core\maps\Schema
{
public $schema = PKPSchemaService::SCHEMA_ANNOUNCEMENT;
public function map(Announcement $item): array
{
// Get the properties defined in the schema
$props = $this->getProps();
return $this->mapByProperties($props, $item);
}
protected function mapByProperties(array $props, Announcement $item): array
{
$output = [];
foreach ($props as $prop) {
switch ($prop) {
// Any property defined in the schema that isn't
// available in the DataObject must be set manually.
case '_href':
$output[$prop] = $this->getApiUrl('announcements/' . $item->getId());
break;
// Get other properties from the DataObject
default:
$output[$prop] = $item->getData($prop);
break;
}
}
return $this->withExtensions($output, $item);
}
}
A schema map will typically implement public methods to map one or many objects, as well as methods to return only the summary properties of the schema.
class Schema extends \PKP\core\maps\Schema
{
/** @var Enumerable */
public $collection;
/** @var string */
public $schema = PKPSchemaService::SCHEMA_ANNOUNCEMENT;
public function map(Announcement $item): array
{
return $this->mapByProperties($this->getProps(), $item);
}
public function summarize(Announcement $item): array
{
return $this->mapByProperties($this->getSummaryProps(), $item);
}
public function mapMany(Enumerable $collection): Enumerable
{
$this->collection = $collection;
return $collection->map(function ($item) {
return $this->map($item);
});
}
public function summarizeMany(Enumerable $collection): Enumerable
{
$this->collection = $collection;
return $collection->map(function ($item) {
return $this->summarize($item);
});
}
/**
* Map schema properties of an Announcement to an assoc array
*/
protected function mapByProperties(array $props, Announcement $item): array
{
$output = [];
...
return $this->withExtensions($output, $item);
}
}
Create a new Map whenever you need to convert to a new output format.
namespace PKP\announcement\maps;
class RSS
{
/** @var DOMDocument */
public $xml;
public function map(Enumerable $announcements): DOMDocument
{
$this->xml = new DOMDocument('1.0');
foreach ($announcements as $announcement) {
$this->mapEach($announcement);
}
return $this->xml->saveXML();
}
protected function mapEach(Announcement $announcement): DOMDocument
{
$root = $this->xml->createElement('item');
$title = $this->xml->createElement('title');
$title->appendChild(
$this->xml->createTextNode(
$announcement->getLocalizedTitle()
)
);
$root->appendChild($title);
$root = $this->withExtensions($root, $announcement);
$this->xml->appendChild($root);
}
}
Maps should always be loaded through the MapContainer
class, which will permit maps to be extended by plugins. Use the app('maps')
helper to load a map through the container.
use PKP\announcement\maps\Schema;
$map = app('maps')->withExtensions(Schema::class);
Maps loaded in this way can be extended by plugins or any third-party code.
use PKP\announcement\maps\Schema;
app('maps')->extend(Schema::class, function($output, Announcement $item, Schema $map) {
// Add days since the announcement was posted
$then = new DateTime($item->getData('datePosted'));
$now = new DateTime();
$output['daysSince'] = (int) $now->diff($then)->format('%a');
return $output;
});
When writing a map, use the withExtensions()
method to return the result for each item so that plugin extensions are applied.
class ExampleMap
{
public function map(Item $item)
{
$output = [$item->id];
return $this->withExtensions($output, $item);
}
}
A Repository should provide a method to load any map related to its entity.
namespace PKP\announcement;
class Repository
{
/**
* Get an instance of the map class for mapping
* announcements to their schema
*/
public function getSchemaMap(): maps\Schema
{
return app('maps')->withExtensions(maps\Schema::class);
}
}
When available, use the Repo facade to load and run maps.
use APP\facades\Repo;
$json = Repo::announcement()
->getSchemaMap()
->map($announcement);
Learn more about the database structure.