[<< previous](15-adding-content.md) | [next >>](17-performance.md) ## Data Repository At the end of the last chapter I mentioned being unhappy with our Pages action, because there is to much stuff happening there. We are firstly receiving some Arguments, then we are using those to query the filesytem for the given page, loading the specific file from the filesystem, rendering the markdown, passing the markdown to the template renderer, adding the resulting html to the response and then returning the response. In order to make our pageaction independent from the filesystem and move the code that is responsible for reading the files to a better place I want to introduce the [Repository Pattern](https://designpatternsphp.readthedocs.io/en/latest/More/Repository/README.html). I want to start by creating a class that represents the Data that is included in a page so that. For now I can spot three distrinct attributes. * the ID (or chapternumber) * the title (or name) * the content Currently all those properties are always available, but we might later be able to create new pages and store them, but at that point in time we are not yet aware of the new available ID, so we should leave that property nullable. This allows us to create an object without an id and let the code that actually saves the object to a persistant store define a valid id on saving. Lets create an new Namespace called `Model` and put a `MarkdownPage.php` class in there: ```php dataPath . '*.md'); if ($files === false) { throw new InternalServerError('cannot read pages'); } return array_map(function (string $filename) { $content = file_get_contents($filename); if ($content === false) { throw new InternalServerError('cannot read pages'); } $idAndTitle = str_replace([$this->dataPath, '.md'], ['', ''], $filename); return new MarkdownPage( (int) substr($idAndTitle, 0, 2), substr($idAndTitle, 3), $content ); }, $files); } public function byName(string $name): MarkdownPage { $pages = array_values( array_filter( $this->all(), fn (MarkdownPage $p) => $p->title === $name, ) ); if (count($pages) !== 1) { throw new NotFound; } return $pages[0]; } } ``` With that in place we need to add the required `$pagesPath` to our settings class and add specify that in our configuration. `src/Settings.php` ```php final class Settings { public function __construct( public readonly string $environment, public readonly string $dependenciesFile, public readonly string $middlewaresFile, public readonly string $templateDir, public readonly string $templateExtension, public readonly string $pagesPath, ) { } } ``` `config/settings.php` ```php return new Settings( environment: 'prod', dependenciesFile: __DIR__ . '/dependencies.php', middlewaresFile: __DIR__ . '/middlewares.php', templateDir: __DIR__ . '/../templates', templateExtension: '.html', pagesPath: __DIR__ . '/../data/pages/', ); ``` Of course we need to define the correct implementation for the container to choose when we are requesting the Repository interface: `conf/dependencies.php` ```php MarkdownPageRepo::class => fn (FileSystemMarkdownPageRepo $r) => $r, FileSystemMarkdownPageRepo::class => fn (Settings $s) => new FileSystemMarkdownPageRepo($s->pagesPath), ``` Now you can request the MarkdownPageRepo Interface in your page action and use the defined functions to get the MarkdownPage Objects. My `src/Action/Page.php` looks like this now: ```php repo->byName($page); // fix the next and previous buttons to work with our routing $content = preg_replace('/\(\d\d-/m', '(', $page->content); assert(is_string($content)); $content = str_replace('.md)', ')', $content); $data = [ 'title' => $page->title, 'content' => $this->parser->parse($content), ]; $html = $this->renderer->render('page/show', $data); $this->response->getBody()->write($html); return $this->response; } public function list(): ResponseInterface { $pages = array_map(function (MarkdownPage $page) { return [ 'id' => $page->id, 'title' => $page->content, ]; }, $this->repo->all()); $html = $this->renderer->render('page/list', ['pages' => $pages]); $this->response->getBody()->write($html); return $this->response; } } ``` Check the page in your browser if everything still works, don't forget to run phpstan and the others fixers before committing your changes and moving on to the next chapter. [<< previous](15-adding-content.md) | [next >>](17-performance.md)