Skip to content

File uploads

Use file() to accept photos and documents. The package builds CodeIgniter's file rules for you, checks files by their real content, and makes the form multipart automatically.

Example

A profile form with a required photo and optional PDF certificates:

app/Forms/ProfileForm.php
<?php

declare(strict_types=1);

namespace App\Forms;

use Ephraitech\Forms\Form;

class ProfileForm extends Form
{
    protected function define(): void
    {
        $this->text('full_name', 'Full name')->rules('required|max_length[100]');

        $this->file('photo', 'Profile photo')
            ->required()
            ->maxSize(2048)
            ->image()
            ->mimes(['image/jpeg', 'image/png', 'image/webp'])
            ->help('JPG, PNG or WebP, up to 2 MB.');

        $this->file('certificates', 'Certificates')
            ->multiple()
            ->maxSize(4096)
            ->extensions(['pdf'])
            ->mimes(['application/pdf'])
            ->help('Optional. PDF files, up to 4 MB each.');

        $this->submit('Save profile');
    }
}

The controller validates as usual, then stores the files:

app/Controllers/Profile.php
<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Forms\ProfileForm;
use CodeIgniter\HTTP\RedirectResponse;

class Profile extends BaseController
{
    public function edit(): string
    {
        return view('profile', ['form' => new ProfileForm()]);
    }

    public function update(): RedirectResponse|string
    {
        $form = new ProfileForm();

        if (! $form->validate()) {
            return view('profile', ['form' => $form]);
        }

        $data = $form->validated();

        // store() moves the file under writable/uploads and returns its path there.
        $data['photo_path'] = $form->getFile('photo')->store('photos');

        $data['certificate_paths'] = [];

        foreach ($form->getFiles('certificates') as $file) {
            $data['certificate_paths'][] = $file->store('certificates');
        }

        session()->setFlashdata('profile', $data);

        return redirect()->to('/profile')->with('message', 'Profile saved.');
    }
}

How it works

Method CodeIgniter rule Checks
required() uploaded A file was uploaded
maxSize(2048) max_size Size in kilobytes
extensions(['pdf']) ext_in The file name's extension
mimes(['application/pdf']) mime_in The file's real content type
image() is_image The file is an image
maxDimensions(800, 600) max_dims Image size in pixels

The helpers also set the input's accept attribute, so the file picker shows matching files first.

Files are separate from data
validated() never contains files. Get them with getFile('photo') (the first file, or null) and getFiles('certificates') (a list), then call store() on each. store('photos') moves the file to writable/uploads/photos/ under a random name and returns the path to save, such as photos/1793219189_42150fa6c630c2ec.png.
Optional fields
An optional file field is only validated when a file was chosen. Once one is, every rule applies.
Multiple files
multiple() accepts several files, submitted as name[]. One invalid file fails the field.
Multipart
A form with a file field gets enctype="multipart/form-data" without you doing anything.
After an error
Browsers cannot refill a file input. When the form comes back with errors, each file field without its own error shows "Please choose the file again."

Common mistakes

Trusting the extension. extensions() reads the file name, which anyone can change: a script renamed to photo.jpg passes it. Always add mimes() or image(), which look at the content.

Allowing SVG as an image. image() accepts SVG files, which can contain JavaScript. If uploaded images are shown back on your site, list the allowed types with mimes() and leave out image/svg+xml.

Expecting the file in validated(). It is not there. Use getFile() and store it yourself.

Forgetting PHP's own limits. upload_max_filesize and post_max_size in php.ini apply before any rule. If they are lower than maxSize(), large files are rejected by PHP first.