My content goes here!
' 40 | ] 41 | ``` 42 | 43 | ## Syntax highlighting 44 | 45 | Thanks to Parsedown, Phpillip supports Github Flavored Markdown. 46 | That means you can define a language for your code blocks: 47 | 48 | ``` php 49 | $this->isPhp(); 50 | ``` 51 | 52 | 53 | ``` javascript 54 | this.isJavascript(); 55 | ``` 56 | 57 | Phpillip provides syntax highlighting for code block that define a language. He entrust [Pygments](http://pygments.org/), a python command line tool, to do the job. 58 | 59 | In order to get that feature, you'll need to install Pygments: 60 | 61 | pip install Pygments 62 | 63 | _Note:_ requires [Python](https://www.python.org/downloads/) 64 | -------------------------------------------------------------------------------- /doc/content/property-handlers.md: -------------------------------------------------------------------------------- 1 | # Property Handlers 2 | 3 | Property handlers are responsible for enriching parsed contents by providing automatic properties or casting properties as a certain type. 4 | 5 | Phpillip provides a default set of Property Handlers (see [Retrieving content](../content/retrieving-content.md)). 6 | 7 | And you're able to add your own to fit your needs! 8 | 9 | ## Create a custom property handler 10 | 11 | _Create a class_ that implements the `Phpillip\Behavior\PropertyHandlerInterface`: 12 | 13 | ``` php 14 | getProperty()]); 45 | } 46 | 47 | /** 48 | * Handle property 49 | * 50 | * @param mixed $value 51 | * @param array $context 52 | * 53 | * @return mixed 54 | */ 55 | public function handle($value, array $context) 56 | { 57 | return $this->doSomethingWith($value); 58 | } 59 | } 60 | ``` 61 | 62 | _Register your property handler_ in the Content Repository: 63 | 64 | ``` php 65 | $app['content_repository']->addPropertyHandler(new MyPropertyHandler()); 66 | ``` 67 | 68 | In this example, the __handle__ method will be called on every _my_property_ property when the content data _isSupported_. 69 | -------------------------------------------------------------------------------- /doc/content/retrieving-content.md: -------------------------------------------------------------------------------- 1 | # Retrieving content 2 | 3 | ## The content repository 4 | 5 | The content repository service is responsible for fetching your content. You'll find it in the Application under the *content_repository* key: 6 | 7 | ``` php 8 | $app['content_repository']; 9 | ``` 10 | 11 | When parsing a content, the repository returns an associative array with the following keys: 12 | 13 | Property | Presence | Description 14 | ------------- | ------------------------ | ----------------------------------- 15 | slug | Added if not provided | Slug, based on the source file name 16 | lastModified | Added if not provided | Last modification of the source file 17 | date | Parsed if provided | If a `date` property exists, parse it as DateTime 18 | weight | Parsed if provided | If a `date` property exists, parse it as DateTime 19 | content | Added for Markdown files | Content of the Markdown file, converted to HTML 20 | ... | Provided | Any other key present in the source file 21 | 22 | > Need more/differents properties? You can [add your own](../content/property-handlers.md) 23 | 24 | ## Fetching content 25 | 26 | ### Get a single content 27 | 28 | The `getContent` method expects a content type and a content name. It returns a single content: 29 | 30 | ``` php 31 | // Get a content matching `my-content.*` contents in 'src/Resources/data/foo': 32 | $app['content_repository']->getContent('foo', 'my-content'); 33 | 34 | // Result: 35 | [ 36 | 'slug' => 'my-content', 37 | 'lastModified' => DateTime, 38 | // ... Any other key present in the source file 39 | ] 40 | ``` 41 | 42 | ### Get all contents 43 | 44 | The `getContents` method expect a content type and return all its contents: 45 | 46 | ``` php 47 | // Get all contents in 'src/Resources/data/foo': 48 | $app['content_repository']->getContents('foo'); 49 | 50 | // Result: 51 | [ 52 | 'my-content' => ['slug' => 'my-content', 'lastModified' => DateTime, ...], 53 | 'my-other-content' => ['slug' => 'my-other-content', 'lastModified' => DateTime, ...], 54 | // ... 55 | ] 56 | ``` 57 | 58 | In a list, the contents are indexed by default by their source file name (a.k.a _slug_). 59 | 60 | ### Indexing and ordering contents 61 | 62 | Say you have a `post` content that contains a `date` property, you can get all the _posts_ indexed by date: 63 | 64 | ``` php 65 | // Get all contents in 'src/Resources/data/post' indexed by 'date': 66 | $app['content_repository']->getContents('post', 'date'); 67 | 68 | // Result: 69 | [ 70 | 1441836000 => ['date' => DateTime, 'slug' => 'my-first-post', ...], 71 | 1443474500 => ['date' => DateTime, 'slug' => 'my-second-post', ...], 72 | // ... 73 | ] 74 | ``` 75 | 76 | A third parameter is provided to sort the resulting array: 77 | 78 | ``` php 79 | // Get older post first ('date' ascending): 80 | $app['content_repository']->getContents('post', 'date', true); 81 | 82 | // Get latest post first ('date' descending): 83 | $app['content_repository']->getContents('post', 'date', false); 84 | ``` 85 | -------------------------------------------------------------------------------- /doc/controller/content.md: -------------------------------------------------------------------------------- 1 | # Content Controller 2 | 3 | Phpillip provides a default `ContentController` that supports 3 actions: 4 | 5 | - __show:__ Display a single content (suited for [single content](../feature/helpers.md#single-content)) 6 | - __list:__ Display a full list of content (suited for [content list](../feature/helpers.md#content-list)) 7 | - __page:__ Display one page of a paginated content list (suited for [pagination](../feature/helpers.md#pagination)) 8 | 9 | ## Show 10 | 11 | To register a controller that displays a single _achievement_: 12 | 13 | ``` php 14 | $this 15 | ->get('/achievements/{achievement}', 'content.controller:show') 16 | ->content('achievement'); 17 | ``` 18 | 19 | The expected template `achievement/show.html.twig` would receive the variable `achievement`. 20 | 21 | ## List 22 | 23 | To register a controller that displays all _achievements_: 24 | 25 | ``` php 26 | $this 27 | ->get('/achievements', 'content.controller:list') 28 | ->contents('achievement'); 29 | ``` 30 | 31 | The expected template `achievement/list.html.twig` would receive the variable `achievements`. 32 | 33 | ## Paginate 34 | 35 | To register a controller that paginates _achievements_: 36 | 37 | ``` php 38 | $this 39 | ->get('/achievements', 'content.controller:page') 40 | ->paginate('achievement'); 41 | ``` 42 | 43 | The expected template `achievement/page.html.twig` would receive the following variables: 44 | - `achievements`: Achievements for the current page 45 | - `page`: Index of the current page 46 | - `pages`: Total number of pages 47 | -------------------------------------------------------------------------------- /doc/controller/custom.md: -------------------------------------------------------------------------------- 1 | # Custom Controller 2 | 3 | You can declare your own controllers as classes: 4 | 5 | ``` php 6 | $app['content_repository']->getContents('product'), 31 | ]; 32 | } 33 | 34 | /** 35 | * Show a product 36 | * 37 | * @param Request $request 38 | * @param Application $app 39 | * @param string $reference 40 | * 41 | * @return array 42 | */ 43 | public function show(Request $request, Application $app, array $reference) 44 | { 45 | return [ 46 | 'products' => $app['content_repository']->getContent('product', $reference), 47 | ]; 48 | } 49 | } 50 | ``` 51 | 52 | Register your controller in the app: 53 | 54 | ``` php 55 | $this->get('/products', 'Controller\\ProductController:index'); 56 | $this->get('/product/{reference}', 'Controller\\ProductController:show'); 57 | ``` 58 | 59 | The expected template `achievement/list.html.twig` would receive the variable `achievements`. 60 | -------------------------------------------------------------------------------- /doc/controller/format.md: -------------------------------------------------------------------------------- 1 | # Route format 2 | 3 | Specifiyng the format of a route will determine the extention of the output file during the build. 4 | 5 | By default, all routes are treated as _HTML_ and therefore dumped as `.html` files. 6 | 7 | Phpillip rely on the _Response_ `Content-Type` header to determine the format of a route. 8 | 9 | To control the output format of a route, you just need to configure the _Response_ with the desired content type. 10 | 11 | There are 3 ways to do it: 12 | 13 | ## Do it, literally: 14 | 15 | ```php 16 | function () { 17 | // Will output a '.txt' file 18 | return new Response('Hello', 200, ['Content-Type' => 'text/plain']); 19 | } 20 | 21 | function () { 22 | // Will output a '.json' file 23 | return new JsonResponse($data); 24 | } 25 | ``` 26 | 27 | ## Set the *_format* attribute of the _Request_ 28 | 29 | In Symfony, _Response_ content type is by default determined by the format of the _Request_. 30 | 31 | So you can define the output format of a route by setting the _Request_ attribute `_format`. Phpillip will provide you with a `format` method on the route to do just that: 32 | 33 | ```php 34 | // Will output a '.txt' file 35 | $app->get('/hello')->format('txt'); 36 | ``` 37 | 38 | __Note__: Remember that the _Response_ expects a Mime-Type (e.g _text/html_) but the _Request_ expects a format (e.g. _html_). 39 | 40 | Finally you can set the format of the route by explicitly naming the file in the url pattern: 41 | 42 | ```php 43 | // Will output a '.json' file 44 | $app->get('/hello.json'); 45 | ``` 46 | 47 | # File name 48 | 49 | Additionaly, you can choose a custom name for the output file. 50 | 51 | With the `setFileName` method: 52 | 53 | ```php 54 | // Will output a '404.html' file 55 | $app->get('/404')->setFilename('404'); 56 | ``` 57 | 58 | Or directly in url pattern: 59 | 60 | ```php 61 | // Will output a 'feed.rss' file 62 | $app->get('/feed.rss'); 63 | ``` 64 | 65 | __Note:__ The default output file name is `index`. 66 | -------------------------------------------------------------------------------- /doc/controller/template.md: -------------------------------------------------------------------------------- 1 | # Template resolution 2 | 3 | Phpillip provides the same type of template resolution that you get in Symfony. 4 | When a Controller doesn't return a _Response_, Phpillip will try to create one by finding and rendering a matching template. 5 | 6 | ## For content routes 7 | 8 | If a route is declared as having a _content_, Phpillip will look for the template: `[content_type]/[show|list].[format].twig` 9 | 10 | ```php 11 | // For single content: 12 | $app->get('/blog/{post}', 'content.controller:show')->content('post'); 13 | 14 | // The template: 'src/Resources/views/post/show.html.twig' 15 | ``` 16 | 17 | 18 | ```php 19 | // For several contents: 20 | $app->get('/blog', 'content.controller:show')->paginate('post'); 21 | // or 22 | $app->get('/blog', 'content.controller:show')->contents('post'); 23 | 24 | // The template: 'src/Resources/views/post/list.html.twig' 25 | ``` 26 | 27 | ## For Class controllers 28 | 29 | If you declare you controller as a Class Controller: 30 | 31 | ```php 32 | $app->get('/blog', 'Acme\Controller\BlogController::index'); 33 | ``` 34 | 35 | Phpillip will look for the template `[ControllerName]/[actionName].[format].twig` (just like Symfony does). 36 | 37 | In our example: `src/Resources/views/Blog/index.html.twig` 38 | 39 | __Note:__ Phpillip looks for a twig template matching the format of your route. 40 | 41 | -------------------------------------------------------------------------------- /doc/feature/helpers.md: -------------------------------------------------------------------------------- 1 | # Route Helpers 2 | 3 | Phpillip's Route extends Silex's Route and provide an extra set of helpers. 4 | 5 | These helpers are designed to save you time by addressing common controller needs automatically: parameters to content conversion, pagination, template resolution... 6 | 7 | ## Content issues 8 | 9 | When building a route that depends on the content (e.g. `/blog/{article}`), Phpillip will need to call the route for each article content you have: _blog/my-first-post_, _blog/my-second-post_... 10 | 11 | That's why Phpillip provides helpers to _link_ your routes to your contents. 12 | 13 | ### Single content 14 | 15 | To tell Phpillip that your route vary over content, use the `content` method on the route: 16 | 17 | ``` php 18 | // For an 'article' content type: 19 | $app->get('/blog/{article}')->content('article'); 20 | ``` 21 | 22 | In this example, Phpillip will call the route for each article file found in `src/Resources/data/article`. 23 | 24 | The _content_ helper also acts as a param converter. 25 | 26 | When a _Request_ hits a route that is declared as having a content, Phpillip will try to fetch the content for you. 27 | 28 | In our previous example, calling the url `/blog/my-first-post` will match our route and Phpillip will look for a file named `src/Resources/data/article/my-first-post.*`. 29 | 30 | If such file exists, Phpillip will parse it as an associative array and set it as an `article` attribute in the _Request_. 31 | 32 | Of course, you can get the automatically fetched content in your controller: 33 | 34 | ``` php 35 | // As a parameter: 36 | function (array $article) { 37 | // Your content is $article 38 | } 39 | 40 | // And in the Request attributes: 41 | function (Request $request) { 42 | $article = $request->attributes->get('article'); 43 | } 44 | ``` 45 | 46 | ### Content list 47 | 48 | To tell Phpillip that your route is a _list_ of contents, use the `contents` methode on the route: 49 | 50 | ``` php 51 | // For an 'article' content type: 52 | $app->get('/blog')->contents('article'); 53 | ``` 54 | 55 | This only acts as a parameter converter. 56 | 57 | When a _Request_ hits a route that is declared as being a _list_ −in our example `/blog`− Phpillip will look for all files in `src/Resources/data/article`. Then it will parse each file as an associative array, store the results in an array and set it as an `articles` attribute in the _Request_. 58 | 59 | To get the automatically fetched contents in your controller: 60 | 61 | ``` php 62 | // As a parameter: 63 | function (array $articles) { 64 | // Your articles are in $articles 65 | } 66 | 67 | // And in the Request attributes: 68 | function (Request $request) { 69 | $articles = $request->attributes->get('articles'); 70 | } 71 | ``` 72 | 73 | By default, the contents are indexed numerically in the array and in the same order as they appear in the file system. 74 | 75 | You can define custom index and sorting in the `contents` method: 76 | The _index_ (string) is a key available in the content. 77 | The _order_ (boolean) is `true` for ascending and `false` for descending. 78 | 79 | To fetch every articles, indexed by _date_ and most recent first (descending): 80 | 81 | ``` php 82 | $app->get('/blog')->contents('article', 'date', false); 83 | ``` 84 | 85 | ### Pagination 86 | 87 | > You need your contents paginated? The method `paginate` works just like the method `contents` but paginate your contents. 88 | 89 | To tell Phpillip that your route is a _paginated list_ of contents, use the `contents` method on the route: 90 | 91 | ``` php 92 | // For an 'article' content type: 93 | $app->get('/blog')->paginate('article'); 94 | ``` 95 | 96 | The paginate helper adds a `page` optional parameter to the route so that it can handle the following urls: `/blog` and `/blog/{page}`. 97 | 98 | When building a route that paginates content, Phpillip will need to call the route for each page, depending on how much content you have: _blog_, _blog/2_, _blog/3_... 99 | 100 | __Note:__ the page parameters is ommitted on the first page. 101 | 102 | The _paginate_ helper then acts as a param converter. 103 | 104 | When a _Request_ hits a route that is declared as being a _paginated list_ −in our example `/blog/2`− Phpillip will count files in `src/Resources/data/article` and parse the subset corresponding to the requested page. 105 | 106 | The _paginate_ helper defines 3 attributes in the _Request_: 107 | 108 | Key | Type | Description 109 | ----------- | -------------------------- | ----------- 110 | `articles` | _array_ | The contents corresponding to the requested page. 111 | `page` | _integer_ | The requested page 112 | `paginator` | _Phpillip\Model\Paginator_ | The total number of pages in the pagination 113 | 114 | Request attributes are available in the controller as always: 115 | 116 | ``` php 117 | // As parameters: 118 | function (array $articles, Paginator $paginator, page) { 119 | // $articles contains only articles of the curent page 120 | } 121 | 122 | // And in the Request attributes: 123 | function (Request $request) { 124 | $articles = $request->attributes->get('articles'); 125 | $paginator = $request->attributes->get('paginator'); 126 | $page = $request->attributes->get('page'); 127 | } 128 | ``` 129 | 130 | ## Hide a route 131 | 132 | To hide a route from the build, use the `hide` method. 133 | 134 | ```php 135 | $app 136 | ->get('_latest-posts') 137 | ->content('post') 138 | ->bind('latest_posts') 139 | ->hide() 140 | ``` 141 | 142 | This can be useful for a route that is meant to be rendered as a part of other routes, but should not generate a static file on its own. 143 | 144 | Like a Twig render: `{{ render(url('latest_posts')) }}` 145 | 146 | ## Route format 147 | 148 | Route format is treated in its [dedicated documentation](../controller/format.md). 149 | 150 | ## Template 151 | 152 | Template resolution is treated in its [dedicated documentation](../controller/template.md). 153 | -------------------------------------------------------------------------------- /doc/feature/sitemap.md: -------------------------------------------------------------------------------- 1 | # Sitemap 2 | 3 | Phpillip automatically generates an XML sitemap of your website. 4 | The sitemap contains every URL called by the _build_ command. 5 | 6 | ## Set last modified 7 | 8 | When registering a url in the sitemap, the build command looks for a _Last-Modified_ header in the _Response_, if it exists it will be used as `#', $value) && preg_match('#