madsynn's avatar

Help saving my form passing it through a upload trait

Hi everyone im stuck and would love your help.

I have a ClipController.php and in the clip it contains relationships to Video.php and Image.php models.

i am able to save the form data just find when i hit cClipController.php store function but here is the catch.

my clip->video form needs to pass through a fileuploadtrait.php but i am unable to get it to work. I am sure its a simple fix but im not seeing it. ill include my code for you to review.

File location paths

use App\Clip;
use App\Video;
use App\Http\Controllers\Traits\FileUploadTrait;
use App\Http\Controllers\ClipController;

Clip controller store funtion

    public function store(StoreClipsRequest $request)
    {
        if (! Gate::allows('clip_create')) {
            return abort(401);
        }

        // This works on the controllers for each individual form.  but not on the combined form here.  example:  on VideoController store this line works fine.
        // $request = $this->saveFiles($request); 

        $clip = Clip::create($request->all());

        foreach ($request->input('videos', []) as $data) {

            // tried these but they dont work
            // $data = $this->saveFiles($request->all());
            // $data = $this->saveFiles($data['videos']['1']['video']);
            // $data = $this->saveFiles(Input::get('video')->get());
            // $request = $this->saveFiles($request->videos()->video); 
            
            $clip->videos()->create($data);
        }
        foreach ($request->input('brands', []) as $data) {
            $clip->brands()->create($data);
        }
        return redirect()->route('admin.clips.index');
    }

Form line in my clip/create.blade.php This part is for the clip => video store functionality

<tr data-index="{{ $index }}">
    <td>{!! Form::hidden('videos['.$index.'][video]', old('videos['.$index.'][video]')) !!}{!! Form::file('videos['.$index.'][video]', ['class' => 'form-control']) !!}</td>
    <td>{!! Form::text('videos['.$index.'][name]', old('videos['.$index.'][name]', isset($field) ? $field->name: ''), ['class' => 'form-control']) !!}</td>
    <td>{!! Form::text('videos['.$index.'][extention]', old('videos['.$index.'][extention]', isset($field) ? $field->extention: ''), ['class' => 'form-control']) !!}</td>
    <td>{!! Form::text('videos['.$index.'][ad_duration]', old('videos['.$index.'][ad_duration]', isset($field) ? $field->ad_duration: ''), ['class' => 'form-control']) !!}</td>

    <td>
        <a href="#" class="remove btn btn-xs btn-danger">remove</a>
    </td>
</tr>

this form saves just fine into the video table just fine but its not using the FileUploadTrait.php file it needs.

FileUploadTrait.php

namespace App\Http\Controllers\Traits;

use Illuminate\Http\Request;
use Intervention\Image\Facades\Image;
use File;
use FFMpeg;
use FFMpeg\FFProbe;
use Illuminate\Support\Facades\Log;
use App\Helpers\Normalize;
use App\Helpers\FFMPEG_helpers;
use App\Clip;
use App\Video;

trait FileUploadTrait
{

    /**
     * File upload trait used in controllers to upload files
     */
    public function saveFiles(Request $request)
    {

        if (! file_exists(public_path().'/uploads')) { File::makeDirectory(public_path().'/uploads',0777, true);}
 
        $uploadPath = env('UPLOAD_PATH', 'uploads');

        $finalRequest = $request;

        foreach ($request->all() as $key => $value) {
            if ($request->hasFile($key)) {
         
                    $filename = $request->file($key)->getClientOriginalName();
                    $extension = $request->file($key)->getClientOriginalExtension();
                    if(preg_match('/^.*\.(mp4|mov|mpg|mpeg|wmv|mkv)$/i', $filename)){
                        //Log::info('passed valication: '.$filename);
                        $filename = $request->file($key)->getClientOriginalName();
                        $basename = substr($filename, 0, strrpos($filename, "."));
                        $basename = Normalize::titleCase($basename);
                        $ad_duration = FFMPEG_helpers::getDuration($request->file($key));

                        $filename = str_slug($basename) . '.' . $extension;
                        $request->file($key)->move($clipPath, $filename);
                        $finalRequest = new Request(array_merge($finalRequest->all(), [$key => $filename, 'video' => $request->video, 'extention' => $extension, 'name'=> $basename, 'ad_duration'=>$ad_duration]));

                        $file_w_path = $clipPath . "/" . $filename;
                    }
                }
            }
        }

        return $finalRequest;
    }
}
0 likes
11 replies
Cronix's avatar

my clip->video form needs to pass through a fileuploadtrait.php

Mind explaining that? A trait is basically like a glorified include(). It just brings other methods into the current class.

use App\Http\Controllers\Traits\FileUploadTrait;

You included the trait, but did you use it in the class?

Should be like

use App\Http\Controllers\Traits\FileUploadTrait;

class YourClass extends Something {
    use FileUploadTrait;  // use the trait

    public function SomeMethod() {
        // now you can use $this->MethodDefinedInTrait();
    }
}
1 like
madsynn's avatar

@Cronix

The trait is used in the top of the controller do i need to move it to the model?



namespace App\Http\Controllers\Admin;

use App\Clip;
use App\Video;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\StoreClipsRequest;
use App\Http\Requests\Admin\UpdateClipsRequest;
use Yajra\DataTables\DataTables;
use App\Http\Controllers\Traits\FileUploadTrait;
use Input;

class ClipsController extends Controller
{
    use FileUploadTrait;

I know where the problem is i just dont know how to fix it. I think its in the store function of the controller.


        foreach ($request->input('videos', []) as $data)            
            $clip->videos()->create($data);
        }

Everything i try to get it to pass thought / use the trait fails.

            // tried these but they dont work
            // $data = $this->saveFiles($request->all());
            // $data = $this->saveFiles($data['videos']['1']['video']);
            // $data = $this->saveFiles(Input::get('video')->get());
            // $request = $this->saveFiles($request->videos()->video); 

error i keep getting is

Argument 1 passed to App\Http\Controllers\Admin\ClipsController::saveFiles() must be an instance of Illuminate\Http\Request, array given, called in /home/vagrant/repos/gui/app/Http/Controllers/Admin/ClipsController.php
Snapey's avatar

Your trait is included in the controller.

It looks like this;

public function saveFiles(Request $request)

So you need to use it like this

$output = $this->saveFiles($request)

(passing in a request object)

madsynn's avatar

HI @Snapey !! How are you. Good to hear from you.

So do you mean something like this?

        foreach ($request->input('videos', []) as $data) {
 
            $data = $this->saveFiles($request)

            $clip->videos()->create($data);

        }

new error from trying that is

Argument 1 passed to Illuminate\Database\Eloquent\Relations\HasOneOrMany::create() must be of the type array, object given, called in /home/vagrant/repos/gui/app/Http/Controllers/Admin/ClipsController.php on line 220
Snapey's avatar

Well I don't know where your trait originated from and it seems quite complex, processing each of the files and returning some modified request object that contains the filenames of the uploaded videos.

This would seem to suggest that you don't need to put it in a loop, just call it and then process what you get back after it has saved the files.

Its also expecting some specific Eloquent setup?

edit: Sorry, my mistake, its your other controller code that is doing that

madsynn's avatar

@Snapey

When o go to video and add one manually it runs through the fileuploadtrait.php and does what its needing to do but when im on clip and ad the video to it it ads the information from the form just fine also problem is that the trait fills in the fields when it processes the video file. The fields such as duration and such are filled in when it runs through the ffmpeg filters to obtain that data. it also saves the extension and filename form fileinfo.

Attacking the video from the controller is the only way its going to be done. video crud is not part of it i only made it to test it out.

How would I go about doing it the way you said after save for instance. can you provide me a example please?

when i use

        foreach ($request->input('videos', []) as $data) {
            $output = $this->saveFiles($request);
            $clip->videos()->create($data);
        }

it saves the info i manually add to the form but its not being processed inside the fileuploadtrait.php like i need to. on upload all fields for video table are filled out by the trait function.

Cronix's avatar

Your trait is only setup to receive the entire Request object, not a part of it.

// $data = $this->saveFiles($request->all());
            // $data = $this->saveFiles($data['videos']['1']['video']);
            // $data = $this->saveFiles(Input::get('video')->get());
            // $request = $this->saveFiles($request->videos()->video); 

So unless you're just passing $request (not request->toArray()) or any other way, it won't work as-is.

foreach ($request->input('videos', []) as $data) {
    $output = $this->saveFiles($request);
    $clip->videos()->create($data);
}

That will save the same thing over and over again, because it's always just using the $request object (in a loop).

I believe what you should be doing is just

// not in loop
$output = $this->saveFiles($request);

And you need to make your saveFiles() method also check whether the $key is a single file, or an array of files. If it's an array you'd need to loop and process each file individually.

if ($request->hasFile($key)) {

    // is it an array of files? Loop through and process individually
    // if not, just process individually.

I hope that makes some sense.

madsynn's avatar

@Cronix @Snapey Thanks guys but no luck.

I have tried all the things you said to try but still no luck.

@Snapey can you give me an example of you idea for having the file save and them run the file.

foreach ($request->input('videos', []) as $data) {
    
    $clip->videos()->create($data);
}


Snapey's avatar

I'm not sure what you mean?

How did you come by this trait? Is it your own work?

madsynn's avatar

@Snapey

I found it i think on github but modified it to do the things i needed. I am not sure how to upload a file using it though. maybe you have a better way.

relationships

CLIP
 ╚═╦═ video 
   ╚═ images

each clip will have one video and multiple images each video has processing it needs when its uploaded or after (this is where i am stuck)

when i add a new clip and use the form to upload a file it works fine but the processing i need done on the file does not happen.

That is unless i use the video crud instead of the parent clip crud. then it uses the trait just fine.

For the life of me i cannot get it to use the trait when created from the clip.create

any help would still be apprecited

Cronix's avatar
Cronix
Best Answer
Level 67

Ditch the trait for now. Get it to work purely in the controller. Once it's working, you'll see what you need to do to port it to the trait.

Right now the trait is only designed to work with a single file in the Request object. That works for the other things you're doing, but not with this array.

{!! Form::file('videos['.$index.'][video]', ['class' => 'form-control']) !!}

The trait needs to be able to handle a single file object (like it does now), or an array of file objects in the Request such as you have in that form code.

Please or to participate in this conversation.