RStreignard's avatar

Is there a bulletproof method for image upload and keep it as easy, as MyModel::create($request->all());

Hi all, I'm a Laravel newbie who would like to learn how to initially stick to best practices.

I've read a lot of image upload tutorials, but there is one thing that I can't make out. For example, here www.easylaravelbook.com/blog/2015/04/08/processing-file-uploads-with-laravel-5/ we have such piece of code:

$product = new Product(array(
  'name' => $request->get('name'),
  'sku'  => $request->get('sku')
));

$product->save();

$imageName = $product->id . '.' . 
    $request->file('image')->getClientOriginalExtension();

$request->file('image')->move(
    base_path() . '/public/images/catalog/', $imageName
);

return \Redirect::route('admin.products.edit', 
    array($product->id))->with('message', 'Product added!');  

But is it correct? Do I really have to list all my model properties like this:

 $product = new Product(array(
  'name' => $request->get('name'),
  'sku'  => $request->get('sku')
));

But I have lots of them, and this looks like much more conveniently:

Work::create($request->all());

Do I miss anything important?

0 likes
7 replies
bobbybouwmann's avatar

You can of course use the create method instead. However the upload part is a bit more tricky. Most developers would create a service class for it. Now you can call that class in your controller and just pass the file and the location for example

RStreignard's avatar

Dear bobbybouwmann, that was the question. I do understand, that using:

Work::create($request->all());

would save some file temp name, instead of that one, that you wish (timestamp()+originalFileName, for ex.)

That's why, they manually assign all model properties, except that one, that should contain the proper file name, but that leads to some long code in some cases, especially when there are a lot of such properties.

So, what is the best way, with the exception of a service class, to mass assign all properties and store a correct file name in db?

I can imagine only one way:

$work = Work::create($request->all());

// some staff with image renaming

$work->image = $preparedFileName;

$work->save();

But it forces us to save the same model twice, and I'm not sure if it is a 'laravel-wayish'?

More often I see a code like:

$product = new Product(array(
'prop1' => $request->get('prop1'),
'prop2'  => $request->get('prop2'),
....
));


$product->image = $preparedFileName;

$product->save();

But what if we have about 10, or dozens properties?

bobbybouwmann's avatar

You can do something like this

// Do your file upload stuff and you get a $filename back
$filename = 'somefilename.jpg';

// Add the file to the request
$request->merge([
    'file' => $filename 
]);

Product::create($request->all());
1 like
RStreignard's avatar

That looks promising. As for as I understand, $request->merge replaces an incoming value with a new one?

And, by the way, is it ok to move the file into folder before actual saving the model?

Looks like it didn't work for me:

if($request->hasFile('image')) {
        $file = Input::file('image');
        $timestamp = str_replace([' ', ':', '-'], '', Carbon::now()->toDateTimeString());
        $name = $timestamp . '_' . $file->getClientOriginalName();
        $file->move(public_path() . '/uploads/images/', $name);

        $request->merge([
            'image' => $name,
        ]);
    }

    Work::create($request->all());

saved it as C:\OpenServer\userdata\temp\php3D5D.tmp

bobbybouwmann's avatar

You can upload the image like so

$file->move(public_path('uploads/images'), $name);

Is the upload part not working? Or what isn't working?

1 like
martinbean's avatar
Level 80

@RStreignard You could do something like this:

// If create(), instantiate new model instance
$model = new ModelName;

// If update(), either use route–model binding to get model instance injected into controller action
// Or find model from database
$model = ModelName::findOrFail($id);

// Fill model with data from request
$model->fill($request->all());

// Do image upload logic…
$model->image_filename = $filename;

// Save model
$model->save();

As you can see, this works whether the model is being created or updated, so you can wrap everything from the fill() call in a service class.

class SaveModelWithImage
{
    public function __construct(ModelName $model)
    {
        $this->model = $model;
    }

    public function save(Request $request)
    {
        $this->model->fill($request->all());

        if ($request->hasFile('file_input_name')) {
            $this->model->image_filename = $this->uploadFile($request->file('file_input_name'));
        }

        return $this->model->save();
    }

    protected function uploadImage(UploadedFile $file)
    {
        // TODO: Generate filename

        // Move file to path of your choice
        $file->move(storage_path('app'));

        // Return generated filename
        return $filename;
    }
}
1 like
RStreignard's avatar

Dear @bobbybouwmann and @martinbean

I do very appreciate your help.

@bobbybouwmann --> Is the upload part not working? Or what isn't working?

The file uploaded with correct name, but saved it into the db with a temp one, as I told: php3D5D.tmp

Looks like $request->merge function hasn't worked.

@martinbean Thank you, I would definitly try this approach!

Please or to participate in this conversation.