Skip to content

Save and redirect ​

This example creates an article with CakePHP model validation and redirects to its detail page. Failed validation keeps the entity and its errors in the form.

Use the installation setup, then create the articles table and ArticlesTable with the files below. Supply a normal CakePHP route named articles:view accepting an id parameter, with its controller and detail template. For protected records, authorize creation and viewing in the application.

php
<?php
declare(strict_types=1);

namespace App\Island;

use Cake\Datasource\EntityInterface;
use Cake\ORM\Locator\LocatorAwareTrait;
use Interact\Attribute\Action;
use Interact\Attribute\State;
use Interact\Island;

/**
 * Creates an article with normal model validation and navigates to its detail page.
 *
 * Save as src/Island/ArticleRedirectIsland.php.
 */
class ArticleRedirectIsland extends Island
{
    use LocatorAwareTrait;

    #[State(writable: true)]
    public string $title = '';

    protected ?EntityInterface $article = null;

    /**
     * Keep failed entity errors on the form, or redirect after saving succeeds.
     */
    #[Action]
    public function save(): void
    {
        $articles = $this->fetchTable('Articles');
        $this->article = $articles->newEntity(['title' => $this->title]);
        if (!$articles->save($this->article)) {
            $this->setErrors($this->article->getErrors() ?: ['_form' => ['Unable to save the article.']]);

            return;
        }

        $this->redirect(['_name' => 'articles:view', 'id' => $this->article->get('id')]);
    }

    /**
     * Preserve the entity's validation context without transporting it to the browser.
     *
     * @return array{article: EntityInterface} Request-local form entity.
     */
    public function viewData(): array
    {
        return ['article' => $this->article ?? $this->fetchTable('Articles')->newEmptyEntity()];
    }
}
php
<?php
declare(strict_types=1);
/**
 * templates/island/article_redirect.php
 *
 * @var \Interact\View\TemplateView $this
 * @var \Cake\Datasource\EntityInterface $article
 * @var string $title
 */
?>
<?= $this->Form->create($article, ['url' => false, 'x-interact:submit' => '$server.call("save")']) ?>
    <?= $this->Form->control('title', ['value' => $title, 'x-interact:model' => 'title']) ?>
    <?= $this->Form->button('Save and view article') ?>
<?= $this->Form->end() ?>
<p role="alert" x-text="$server.error ? $server.error.message : ''"></p>
php
<?php
declare(strict_types=1);

namespace App\Model\Table;

use Cake\ORM\Entity;
use Cake\ORM\Table;
use Cake\Validation\Validator;

/**
 * Validates article titles before creating records.
 *
 * Save as src/Model/Table/ArticlesTable.php.
 */
class ArticlesTable extends Table
{
    /**
     * Use CakePHP's generic entity for this example's single editable column.
     */
    public function initialize(array $config): void
    {
        parent::initialize($config);
        $this->setEntityClass(Entity::class);
    }

    /**
     * Require a title that fits the database column.
     */
    public function validationDefault(Validator $validator): Validator
    {
        return $validator->requirePresence('title', 'create')
            ->notEmptyString('title', 'Enter a title.')
            ->maxLength('title', 120, 'Use up to 120 characters.');
    }
}
sql
CREATE TABLE articles (id INTEGER PRIMARY KEY, title VARCHAR(120) NOT NULL);
php
<?= $this->Interact->render('ArticleRedirect') ?>

redirect() skips the final island render. No redirect directive or browser callback is needed. See redirects for routing defaults, external URLs, history replacement, and lifecycle rules.

The larger demo at /outcomes also provides save-and-redirect controls, including history replacement, alongside native downloads.