25
Jul '18

I know I know. Rely too much on traits and you’re asking for trouble. But when used correctly, for self-contained bolt-on functionality they really can be fantastic and save loads of time.

There only condition for this logic is that any record with which you are using this must have a ‘logo’ field. Yeah I know I could have done a more advanced version with a separate logo table and a polymorphic relationship, but it’s not like a standard system will have 2 dozen records that all require logos against them so I’ve kept it simple.

I’ve used the word ‘logo’ here, but it’s a generic image storing trait and could be used for anything. You’ll also notice an optional config option of ‘app.directory.logo’, which, if provided, will be used as a relative path below the application’s public folder, otherwise ‘/logos’ will be used.

This assumes that you have an image referenced as ‘logo_file’ in your request and have triggered a save on the parent record.

namespace App\Trt;

trait Logo
{
    /**
     * Custom boot method
     */
    public static function bootLogo()
    {
        /*
         * Trigger on save
         */
        static::saved(function ($record) {

            try {
                if(request()->hasFile('logo_file') && request()->file('logo_file')->isValid()) {
                    $logo_file = request()->file('logo_file');
                    $logo_folder = public_path(config('app.directory.logo', '/logos'));

                    if(is_dir($logo_folder) && is_writable($logo_folder)) {
                        //Prepending the record ID to ensure uniqueness
                        //Could also prepend parent record type / sanitised model name if it's to be used for multiple records
                        $file_name = $record->id . '_' . $logo_file->getClientOriginalName();

                        $record->clearLogo(false);

                        if($logo_file->move($logo_folder, $file_name)) {
                            $record->logo = $file_name;

                            //Direct update here so we don't trigger any callbacks
                            \DB::table($record->getTable())->where('id', $record->id)->update(['logo' => $file_name]);
                        }
                    }
                    else {
                        \Log::error('Logo folder is not writable ' . $logo_folder);
                    }
                }
                elseif(request()->clear_logo) {
                    $record->clearLogo();
                }
            }
            catch(\Exception $e) {

            }
        });

        /*
         * Trigger on delete
         */
        static::deleting(function ($record) {
            $record->clearLogo(false);
        });
    }

    /**
     * Check for logo
     * @return boolean
     */
    public function hasLogo() {
        return ($this->logo && is_file($this->logoPath()));
    }

    /**
     * Logo URL
     * @return string|false
     */
    public function logoURL() {
        return $this->logo ? url(config('app.directory.logo', '/logos') . '/' . $this->logo) : false;
    }

    /**
     * Logo Path
     * @return string|false
     */
    public function logoPath() {
        return $this->logo ? public_path(config('app.directory.logo', '/logos') . '/' . $this->logo) : false;
    }

    /**
     * Clear Logo
     * @param boolean [$save=true] Save
     * @return boolean
     */
    public function clearLogo($save = true) {
        if($this->logo) {
            if($this->hasLogo())
                unlink($this->logoPath());

            $this->logo = null;

            if($save)
                $this->save();

            return true;
        }

        return false;
    }
}

And that’s it!