Invoice download
This example updates a request count and downloads a public sample invoice without leaving the page. The file is served by an ordinary CakePHP controller.
Use the installation setup and normal application attribute routing. Save each file at the path in its docblock. The sample is public; real invoice endpoints must authorize access to the requested record independently.
php
<?php
declare(strict_types=1);
namespace App\Island;
use Interact\Attribute\Action;
use Interact\Attribute\State;
use Interact\Island;
/**
* Updates an export count while asking the browser to download a public sample invoice.
*
* Save as src/Island/InvoiceDownloadIsland.php.
*/
class InvoiceDownloadIsland extends Island
{
#[State]
public int $exports = 0;
/**
* Render the new count and request a native download from a separate application route.
*/
#[Action]
public function download(): void
{
$this->exports++;
$this->downloadUrl(['_name' => 'invoices:download']);
}
}php
<?php
declare(strict_types=1);
/**
* templates/island/invoice_download.php
*
* @var \Interact\View\TemplateView $this
* @var int $exports
*/
?>
<?= $this->Form->button('Download invoice', ['type' => 'button', '@click' => '$server.call("download")']) ?>
<p role="status">Requested <?= $exports ?> downloads.</p>
<p role="alert" x-text="$server.error ? $server.error.message : ''"></p>php
<?php
declare(strict_types=1);
namespace App\Controller;
use Cake\Controller\Controller;
use Cake\Http\Response;
use Cake\Routing\Attribute\Get;
/**
* Serves a public sample CSV through CakePHP's normal attachment response.
* Real invoice endpoints must authorize the requested record independently.
*
* Save as src/Controller/InvoicesController.php.
*/
class InvoicesController extends Controller
{
/**
* Let the browser handle the attachment name and transfer outside the island update.
*/
#[Get('/invoices/sample', name: 'invoices:download')]
public function download(): Response
{
return $this->getResponse()
->withType('text/csv')
->withDownload('invoice.csv')
->withStringBody("Item,Amount\nWorkshop,125.00\n");
}
}php
<?= $this->Interact->render('InvoiceDownload') ?>The browser requests /invoices/sample after the island renders the new count. CakePHP's withDownload() supplies the filename. For a protected example with current-permission checks, open /outcomes in the larger demo.
See downloads for destination rules, file responses, and the limits of transfer feedback.