Keep Books views together
src/views/books/index.php
src/views/books/create.php
src/views/books/edit.phpThe controller path is always relative to src/views. Controller calls must therefore be updated.
return $this->view('books/index.php', ['books' => $books]);
return $this->view('books/create.php', ['errors' => [], 'old' => []]);
return $this->view('books/edit.php', ['book' => $book, 'errors' => []]);Extract the shared form
src/views/partials/book-form.php
<label for="title">Title</label>
<input id="title" name="title" type="text" required
value="<?= $view->escape($values['title'] ?? '') ?>">
<?php foreach ($errors['title'] ?? [] as $error): ?>
<p class="field-error" role="alert"><?= $view->escape($error) ?></p>
<?php endforeach; ?>
<label for="author">Author</label>
<input id="author" name="author" type="text" required
value="<?= $view->escape($values['author'] ?? '') ?>">
<?php foreach ($errors['author'] ?? [] as $error): ?>
<p class="field-error" role="alert"><?= $view->escape($error) ?></p>
<?php endforeach; ?>Use it for create and edit
<form method="post" action="<?= $view->escape($action) ?>">
<?= $view->csrfField() ?>
<?php $view->partial('book-form.php', [
'values' => $values,
'errors' => $errors,
]) ?>
<button type="submit"><?= $view->escape($submitLabel) ?></button>
</form>The controller passes action, values, and submitLabel. The same partial displays fields; the page remains responsible for submission address and button text.
Practical rule
Extract a partial when the same block truly appears on several pages or represents a clear unit such as navigation. Do not split every element into a file: too many tiny files make the flow harder to follow.