Fi lets you query a collection of text files, as if the folder of files were a database (well, almost).
Fi (rhymes with pie) is designed to be used as part of a static site generator.
There is a documented, runnable example you can play with:
$ git clone https://github.com/yuanqing/fi
$ cd fi
$ composer install
$ php example/example.php
There are also tests.
Suppose we have organised our text files into neat date-based folders like so:
data/
|-- _defaults.md
`-- 2014/
|-- 01/
| |-- _defaults.md
| |-- 01-foo.md
| |-- 02-bar.md
| `-- ...
|-- 02/
| `-- ...
`-- ...
Each text file contains YAML frontmatter and content. The file 01-foo.md
might be something like:
---
title: foo title
---
foo content
We would query our data
directory like so:
$dataDir = 'data';
$filePathFormat = '{{ date.year: 4d }}/{{ date.month: 2d }}/{{ date.day: 2d }}-{{ title: s }}.md';
$collection = Fi::query($dataDir, $filePathFormat); #=> Collection object
Every file that matches the given $filePathFormat
is a Document. A Collection, then, is simply an Iterator over a set of Documents:
foreach ($collection as $document) {
$document->getFilePath(); #=> 'data/2014/01/01-foo.md', ...
$document->getField('title'); #=> 'foo title', ...
$document->getField('date'); #=> ['year' => 2014, 'month' => 1, 'day' => 1], ...
$document->getContent(); #=> 'foo content', ...
}
We can also access a Document directly by index:
$document = $collection->getDocument(0); #=> Document object
$document->getFilePath(); #=> 'data/2014/01/01-foo.md'
$document->getField('title'); #=> 'foo title'
$document->getField('date'); #=> ['year' => 2014, 'month' => 1, 'day' => 1]
$document->getContent(); #=> 'foo content'
Fi also supports map, filter, and sort operations over our Collection of Documents:
# set the date to a DateTime object
$collection->map(function($document) {
$date = DateTime::createFromFormat('Y-m-d', implode('-', $document->getField('date')));
return $document->setField('date', $date);
});
# filter out Documents with date 2014-01-01
$collection->filter(function($document) {
return $document->getField('date') != DateTime::createFromFormat('Y-m-d', '2014-01-01');
});
# sort by date in descending order
$collection->sort(function($document1, $document2) {
return $document1->getField('date') < $document2->getField('date');
});
A text file will inherit default values (for fields or content) from any _defaults.md
file found in the same directory, or in a parent directory. Defaults are said to cascade; _defaults.md
files found further down the file hierarchy will overwrite those higher up the hierarchy.
Fi::query ( string $dataDir, string $filePathFormat [, string $defaultsFileName = '_defaults.md' ] )
Makes a Collection object.
$dataDir = './data';
$filePathFormat = '{{ year: 4d }}/{{ month: 2d }}/{{ date: 2d }}-{{ title: s }}.md';
$collection = Fi::query($dataDir, $filePathFormat);
-
$dataDir
is the directory where Fi will look for text files that match the$filePathFormat
. -
$filePathFormat
is specified using a Regex-like syntax; see Extract.php. -
$defaultsFileName
is the name of the text file that Fi will look for when resolving defaults.
Applies the $callback
to each Document in the Collection. Returns the Collection object.
# sets the title of all Documents to 'foo'
$collection->map(function($document) {
$document->setField('title', 'foo');
return $document;
}); #=> Collection
$callback
takes a single argument of type Document. It must return an object of type Document.
Filter out Documents in the Collection using the $callback
. Returns the Collection object.
# filters out Documents with the title 'foo'
$collection->filter(function($document) {
return $document->getField('title') !== 'foo';
}); #=> Collection
$callback
takes a single argument of type Document. Return false to exclude that Document from the Collection.
Sorts the Collection using the $callback
. Returns the Collection object.
# sorts by title in ascending order
$collection->sort(function($document1, $document2) {
return strnatcasecmp($document1->getField('title'), $document2->getField('title'));
}); #=> Collection
$callback
takes two arguments of type Document. Return1
if the first Document argument is to be ordered before the second, else return-1
.
Sorts the Collection by the $fieldName
in the specified $sortOrder
. Returns the Collection object.
# sorts by title in ascending order
$collection->sort('title', Fi::ASC); #=> Collection
# sorts by title in descending order
$collection->sort('title', Fi::DESC); #=> Collection
$sortOrder
must be eitherFi::ASC
orFi::DESC
.
Gets all the Documents in the Collection as an array.
$collection->toArr(); #=> [Document, Document, ...]
Gets the file path of the text file (relative to the $dataDir
) that corresponds to the Document.
$document->getFilePath(); #=> 'data/2014/01/01-foo.md'
Gets all the fields of the Document.
$document->getFields(); #=> ['title' => 'foo', 'date' => ['year' => 2014, 'month' => 1, 'day' => 1]]
Checks if the Document has a field with the specified $fieldName
.
$document->hasField('title'); #=> true
Gets the value of the specified $fieldName
.
$document->getField('title'); #=> 'foo'
Sets the field with $fieldName
to the specified $fieldValue
. Returns the Document object.
$document->setField('title', 'bar'); #=> Document
Checks if the Document content is non-empty.
$document->hasContent(); #=> true
Gets the Document content.
$document->getContent(); #=> 'bar'
Sets the Document content to the specified $content
. Returns the Document object.
$document->setContent('baz'); #=> Document
Fi requires at least PHP 5.3 or HHVM, and Composer.
-
Install Composer.
-
Install the Composer package:
$ composer require yuanqing/fi ~0.2
-
In your PHP file, require the Composer autoloader:
require_once __DIR__ . '/vendor/autoload.php';
You need PHPUnit to run the tests:
$ git clone https://github.com/yuanqing/fi
$ cd fi
$ composer install
$ phpunit