Optional email attachments

I followed the Cookbook post on sending forms with file attachments. However, I now have the problem that no mails are delivered if no file is selected. I found out that if ($uploads[0]['error'] !== 4) checks if files are selected. However, apparently I still missed something. If a file is selected, the sending works without problems.

Here is my controller. I am grateful for any advice!

<?php

return function($kirby, $page) {

    if ($kirby->request()->is('POST') && get('submit')) {

        // initialize variables
        $alerts      = null;
        $success      = null;

        // check the honeypot
        if (empty(get('website')) === false) {
            go($page->url());
            exit;
        }

  
        // get the data and validate the other form fields
        $data = [
            'name'          => get('name'),
            'organization'  => get('organization'),
            'city'          => get('city'),
            'phone'         => get('phone'),
            'email'         => get('email'),
            'message'       => get('message'),
            'receipient'    => get('receipient')
        ];

        $rules = [
            'message'   => ['required', 'min' => 1, 'max' => 3000],
        ];

        $messages = [
            'message'   => 'Your message has to be between 10 and 3000 Characters.'
        ];

        // SPAM-Protection Timestamp
        if (time() - get('time') < 5){
            go('spam');
            die();
         }

        // Which receipient?
        if($data['receipient']      == '01'): $receipient = 'mail1@mail.com';
        elseif($data['receipient']  == '02'): $receipient = 'mail2@mail.com';
        elseif($data['receipient']  == '03'): $receipient = 'mail3@mail.com';
        elseif($data['receipient']  == '04'): $receipient = 'mail4@mail.com';
        elseif($data['receipient']  == '05'): $receipient = 'mail5@mail.com';
        elseif($data['receipient']  == '06'): $receipient = 'mail6@mail.com';
        endif;

        // get the uploads
        $uploads = $kirby->request()->files()->get('file');

        // we want no more than 3 files
        if (count($uploads) > 3) {
            $alerts[] = 'Please upload max 3 files.';
        }

        if ($uploads[0]['error'] !== 4) {
            foreach ($uploads as $upload) {
                
                //  make sure there are no other errors  
                if ($upload['error'] !== 0) {
                    $alerts[] = 'The file could not be uploaded';
                // make sure the file is not larger than 2MB…    
                } elseif ($upload['size'] > 2000000)  {
                    $alerts[] = $upload['name'] . ' is larger than 2 MB';
                // …and the file is a PDF
                } elseif ($upload['type'] !== 'application/pdf') {
                    $alerts[] = $upload['name'] . ' is not a PDF';
                // all valid, try to rename the temporary file
                } else {
                    $name     = $upload['tmp_name'];
                    $tmpName  = pathinfo($name);
                    // sanitize the original filename
                    $filename = $tmpName['dirname']. '/'. F::safeName($upload['name']);
            
                    if (rename($upload['tmp_name'], $filename)) {
                        $name = $filename;
                    }
                    // add the files to the attachments array
                    $attachments[] = $name;
                }  
            }
        }

        // some of the data is invalid
        if ($invalid = invalid($data, $rules, $messages)) {
            $alerts = $invalid;
        }
    

        // the data is fine, let's send the email with attachments
        elseif (empty($alerts)) {
            $from = new \Kirby\Cms\User([
                'email' => 'info@email.de',
                'name'  => 'My Name',
            ]);
            try {
                $kirby->email([
                    'template' => 'email',
                    'from'     => $from,
                    'to'       => $receipient,
                    'subject'  => 'Contact Form',
                    'data'     => [
                        'message'           => esc($data['message']),
                        'name'              => esc($data['name']),
                        'email'             => esc($data['email']),
                        'organization'      => esc($data['organization']),
                        'city'              => esc($data['city']),
                        'phone'             => esc($data['phone']),
                    ],
                    'attachments' => $attachments
                ]);
            } catch (Exception $error) {
                // we only display a general error message, for debugging use `$error->getMessage()`
                // $alerts[] = "The email could not be sent";
                echo $error->getMessage();
            }

            if (get('email')) {
                // no exception occurred, let's send a success message
                if (empty($alerts)) {
                    // store reference and name in the session for use on the success page
                    try {
                        $kirby->email([
                            'template'  => 'receipient',
                            'from'      => $from,
                            'to'        => $data['email'],
                            'subject'   => 'Contact Form',
                            'data'      => [
                              'message'           => esc($data['message']),
                              'name'              => esc($data['name']),
                              'email'             => esc($data['email']),
                              'organization'      => esc($data['organization']),
                              'city'              => esc($data['city']),
                              'phone'             => esc($data['phone']),
                            ],
                        ]);
                        } catch (Exception $error) {
                            echo $error->getMessage();
                        }
                }
            }

        // no exception occurred, let's send a success message
        if (empty($alert) === true) {
            $success = 'Your message was sent!';
            $data = [];
        }
        }
        
    }

    // return data to template
    return [
        'alerts' => $alerts ?? null,
        'data'   => $data   ?? false,
        'success' => $success ?? false
    ];
};

Here you want to check if you have any uploads instead:

if ( ! empty($uploads) ) {
  // stuff
}

<input name="file[]" type="file" multiple required>

You will also have to remove the required attribute from the file input if you haven’t already.

Unfortunately, it does not work. The error messages appear one after the other as soon as I prepend if ( ! empty($uploads) ). Starting with the file could not be uploaded

I have changed the relevant part of the controller like this:

if ( ! empty($uploads) ) {
    foreach ($uploads as $upload) {
        
        //  make sure there are no other errors  
        if ($upload['error'] !== 0) {
            $alerts[] = 'The file could not be uploaded';
        // make sure the file is not larger than 2MB…    
        } elseif ($upload['size'] > 12000000)  {
            $alerts[] = $upload['name'] . ' is larger than 2 MB';
        // …and the file is a PDF
        } elseif ($upload['type'] !== 'application/pdf') {
            $alerts[] = $upload['name'] . ' is not a PDF';
        // all valid, try to rename the temporary file
        } else {
            $name     = $upload['tmp_name'];
            $tmpName  = pathinfo($name);
            // sanitize the original filename
            $filename = $tmpName['dirname']. '/'. F::safeName($upload['name']);
    
            if (rename($upload['tmp_name'], $filename)) {
                $name = $filename;
            }
            // add the files to the attachments array
            $attachments[] = $name;
        }  
    }
}

The field looks like this:

 <div class="form-element">
      <label for="file">
        <span class="help">Max. 3 files.</span>
      </label>
      <input name="file[]" type="file" multiple>
    </div>

Oh, ok, $uploads always returns an array…, so your original code should be ok.

Do you actually get an error message?

Because I echoed $error->getMessage() I get

Undefined variable $attachments

And when I attach a file it works fine.

You didn’t initialize $attachments = [] like in the recipe, that’s why you get the error.

Thank you very much! That was the reason.