milesj / uploader

[Deprecated] A CakePHP plugin for file uploading and validating.
MIT License
193 stars 73 forks source link

Error 4 #97

Closed jacobroufa closed 11 years ago

jacobroufa commented 11 years ago

So I want to have an upload field that is not required. I've got validation set up though, in the event that someone decides to upload something. I'm getting this 'error' => (int) 4 -- which according to php.net is UPLOAD_ERR_NO_FILE. Which is what I want, right? So how would you suggest I proceed here? I've got your plugin working just fine on another Cake app, but I can't seem to get it to play nice here...

/app/Controller/EssaysController.php (line 59)
array(
    'Essay' => array(
        'student' => 'me',
        'title' => 'this',
        'body' => 'essay!',
        'contact_name' => 'me',
        'contact_phone' => '800-555-1212',
        'headshot' => array(
            'name' => '',
            'type' => '',
            'tmp_name' => '',
            'error' => (int) 4,
            'size' => (int) 0
        )
    )
)
Notice (8): Array to string conversion [CORE/Cake/Model/Datasource/DboSource.php, line 1004]
Code Context
implode - [internal], line ??
DboSource::create() - CORE/Cake/Model/Datasource/DboSource.php, line 1004
Model::save() - CORE/Cake/Model/Model.php, line 1740
EssaysController::add() - APP/Controller/EssaysController.php, line 70
ReflectionMethod::invokeArgs() - [internal], line ??
Controller::invokeAction() - CORE/Cake/Controller/Controller.php, line 485
Dispatcher::_invoke() - CORE/Cake/Routing/Dispatcher.php, line 186
Dispatcher::dispatch() - CORE/Cake/Routing/Dispatcher.php, line 161
[main] - APP/webroot/index.php, line 97
jacobroufa commented 11 years ago

Also, here's what I've set in the model. Sorry I didn't include it above.

   public $actsAs = array(
     'Uploader.Attachment' => array(
       'headshot' => array(
         'allowEmpty' => true,
         'uploadDir' => '/headshots/',
         'transforms' => array(
           'method' => 'resize',
           'width' => 150,
           'dbColumn' => 'headshot'
         )
       )
     ),
     'Uploader.FileValidation' => array(
       'headshot' => array(
         'extension' => array(
           'value' => array('gif', 'jpg', 'jpeg', 'png', 'pjpeg'),
           'error' => 'Image must be in GIF, JPEG or PNG format'
         ),
         'filesize' => array(
           'value' => 3145728,
           'error' => 'Image must be less than 3MB'
         ),
         'required' => false
       )
     )
   );
milesj commented 11 years ago

Is this using v4?

jacobroufa commented 11 years ago

v3.7.4

I'll give v4 a shot.

jacobroufa commented 11 years ago

Ok, so installed v4 now, along with Composer. Thanks for the blog post on that, by the way. :)

The only way I can get anything to show up now is if I remove my transforms from Uploader.Attachment in my model, otherwise I get the error:

Fatal Error
Error: Unsupported operand types    
File: app/Plugin/Uploader/Model/Behavior/AttachmentBehavior.php
Line: 156

If I remove transforms, I can upload files on the initial form (/model/add), but cannot save when I want to edit it afterward. When I try to save an edit I get validation errors.

The thing I'm more concerned about is the transforms though, honestly. Has something changed between 3.7.4 and 4.0.0 that affects transforms?

jacobroufa commented 11 years ago

Transform working -- I had to modify the uploadDir and finalPath in order to get it to play nice. See below. Also, I had to name my transform; I couldn't just have options set on a generic single transform. I wonder if this wasn't my issue to begin with?

'Uploader.Attachment' => array(
  'headshot' => array(
    'allowEmpty' => true,
    'uploadDir' => 'img/headshots/',
    'finalPath' => 'headshots/',
    'transforms' => array(
      'thumb' => array('method' => 'resize', 'width' => 150, 'dbColumn' => 'headshot')
    )
  )
),

Keeping this open until I can resolve my edit validation errors. Also, it might be nice to have some insight as to what may have caused my original error? Would having a malformed 'transforms' => array() do that?

milesj commented 11 years ago

It may have because I have never seen that error before (regarding 4), and the 3.x has had many versions.

In v4 a lot of the settings have changed, so it's best to read the docs carefully: http://milesj.me/code/cakephp/uploader

For example, the uploadDir should probably be an absolute path, and the transform should have a self = true setting. Also the array key for transforms is the database column.

jacobroufa commented 11 years ago

Gotcha. I have been reading the docs, quite thoroughly. Not sure what all applies though, due to no "edited/modified/updated on... " field and the top of the page still references v3.6.3. Thanks for the clarification!

I will set the self = true on my transform and change the key, but I'm curious about the absolute path for the following reason:

Right now I'm working on my app in its dev state, and it will live in a different directory in its release state. In either state the relative path is the same (relative to the app/webroot dir), but the absolute path changes significantly when I go to publish it. Basically, it's just one more thing for me to remember to change when I publish my app. Relative paths seem to work fine -- should I be concerned that keeping it that way will break something at some point?

milesj commented 11 years ago

You could just define a constant: define('UPLOAD_DIR', WWW_ROOT . '/img/headshots/'). It's similar to what I do during setup: https://github.com/milesj/cake-uploader/blob/master/Model/Behavior/AttachmentBehavior.php#L144

The docs will always represent the latest version, I just haven't updated the number to 4 till the docs are done.

jacobroufa commented 11 years ago

Got it. Thanks for the tip there. Also, thanks for being so active in getting back to this issue; would that all maintainers be so vigilant!

My edit validation woes were self-brought. Being fixed now, along with the original request, I see no reason not to close this. Thanks!

milesj commented 11 years ago

No worries, I live for this kind of stuff :P

I also updated the docs more: http://milesj.me/code/cakephp/uploader

jacobroufa commented 11 years ago

Shoot. One last thing. I'm not able to define a constant inside of the variable or within my class at all... Where should I be doing that? Sorry, I'm still pretty new to Cake.

milesj commented 11 years ago

You could place the constant above the class definition, or in a config file.

define('UPLOAD_DIR', WWW_ROOT . '/img/headshots/');

class Model { }

jacobroufa commented 11 years ago

/me facepalms

I did this shortly before posing the question above, but must have had a syntax error or something, and my app freaked out. Yay to being more comfortable with procedural than OOP? Thanks again man, and thanks also for a great plugin!

jacobroufa commented 11 years ago

I'm back. Getting error 4 again when I don't have a file selected to upload. Using the latest version of the plugin -- doublechecked everything against my working setup on another app.

When I attach a file, I get error 0 (which should mean that the file is prepared to upload, right?) but the log suggests it's trying to save to a different controller even though the view is for a method in the proper controller.

From my logs -- the first entry is with a file attached, second with none:

2013-01-16 10:58:53 Error: [MissingControllerException] Controller class HeadshotsController could not be found.
#0 /var/www/vhosts/e-rockford.com/subdomains/beta/httpdocs/apps/gtkm/app/webroot/index.php(97): Dispatcher->dispatch(Object(CakeRequest), Object(CakeResponse))
#1 {main}

2013-01-16 10:59:09 Error: [PDOException] SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Array' in 'field list'
#0 /var/www/vhosts/e-rockford.com/subdomains/beta/httpdocs/apps/gtkm/lib/Cake/Model/Datasource/DboSource.php(459): PDOStatement->execute(Array)
#1 /var/www/vhosts/e-rockford.com/subdomains/beta/httpdocs/apps/gtkm/lib/Cake/Model/Datasource/DboSource.php(425): DboSource->_execute('INSERT INTO `gt...', Array)
#2 /var/www/vhosts/e-rockford.com/subdomains/beta/httpdocs/apps/gtkm/lib/Cake/Model/Datasource/DboSource.php(1007): DboSource->execute('INSERT INTO `gt...')
#3 /var/www/vhosts/e-rockford.com/subdomains/beta/httpdocs/apps/gtkm/lib/Cake/Model/Model.php(1738): DboSource->create(Object(Person), Array, Array)
#4 /var/www/vhosts/e-rockford.com/subdomains/beta/httpdocs/apps/gtkm/app/Controller/PeopleController.php(92): Model->save(Array)
#5 [internal function]: PeopleController->add_step('3')
#6 /var/www/vhosts/e-rockford.com/subdomains/beta/httpdocs/apps/gtkm/lib/Cake/Controller/Controller.php(485): ReflectionMethod->invokeArgs(Object(PeopleController), Array)
#7 /var/www/vhosts/e-rockford.com/subdomains/beta/httpdocs/apps/gtkm/lib/Cake/Routing/Dispatcher.php(186): Controller->invokeAction(Object(CakeRequest))
#8 /var/www/vhosts/e-rockford.com/subdomains/beta/httpdocs/apps/gtkm/lib/Cake/Routing/Dispatcher.php(161): Dispatcher->_invoke(Object(PeopleController), Object(CakeRequest), Object(CakeResponse))
#9 /var/www/vhosts/e-rockford.com/subdomains/beta/httpdocs/apps/gtkm/app/webroot/index.php(97): Dispatcher->dispatch(Object(CakeRequest), Object(CakeResponse))
#10 {main}

Any thoughts?

milesj commented 11 years ago

What are your settings? I'll duplicate it in my tests.

jacobroufa commented 11 years ago

Uploader settings?

define('UPLOAD_DIR', WWW_ROOT . '/img/headshots/');
class Person extends AppModel {
  public $actsAs = array(
    'Uploader.Attachment' => array(
      'headshot' => array(
        'uploadDir' => UPLOAD_DIR,
        'finalPath' => 'headshots/',
        'stopSave' => false,
        'allowEmpty' => true,
        'transforms' => array(
          'full' => array('method' => 'resize', 'width' => 300, 'dbColumn' => 'headshot', 'self' => true),
          'thumb' => array('method' => 'resize', 'width' => 120, 'dbColumn' => 'headshot_thumb')
        )
      )
    ),
    'Uploader.FileValidation' => array(
      'headshot' => array(
        'extension' => array(
          'value' => array('gif', 'jpg', 'jpeg', 'png', 'pjpeg'),
          'error' => 'Image must be in GIF, JPEG or PNG format'
        ),
        'filesize' => array(
          'value' => 5242880,
          'error' => 'Image must be less than 5MB'
        ),
        'required' => false
      )
    )
  );
}
milesj commented 11 years ago

I just used your same exact settings and successfully saved with and without an image for creation and updating. Not sure what you are running into.

jacobroufa commented 11 years ago

I think this may be because I've got the file upload field in the first part of a 3 step form. I got the form to let me not upload a file finally... but now when I try to upload one it tells me the tmp file doesn't exist.

/tmp/phpDG0zNx does not exist
Error: An Internal Error Has Occurred.

Stack Trace
APP/Plugin/Uploader/Model/Behavior/FileValidationBehavior.php line 372 → Transit\File->__construct(string)
APP/Plugin/Uploader/Model/Behavior/FileValidationBehavior.php line 106 → FileValidationBehavior->_validate(Person, array, string, array)
[internal function] → FileValidationBehavior->filesize(Person, array, integer, array)
CORE/Cake/Model/BehaviorCollection.php line 238 → call_user_func_array(array, array)
CORE/Cake/Model/Model.php line 795 → BehaviorCollection->dispatchMethod(Person, string, array)
[internal function] → Model->__call(string, array)
[internal function] → Person->filesize(array, integer, array)
CORE/Cake/Model/Validator/CakeValidationRule.php line 277 → call_user_func_array(array, array)
CORE/Cake/Model/Validator/CakeValidationSet.php line 136 → CakeValidationRule->process(string, array, array)
CORE/Cake/Model/ModelValidator.php line 259 → CakeValidationSet->validate(array, boolean)
CORE/Cake/Model/ModelValidator.php line 99 → ModelValidator->errors(array)
CORE/Cake/Model/Model.php line 3047 → ModelValidator->validates(array)
CORE/Cake/Model/ModelValidator.php line 134 → Model->validates(array)
CORE/Cake/Model/Model.php line 2356 → ModelValidator->validateAssociated(array, array)
CORE/Cake/Model/ModelValidator.php line 211 → Model->validateAssociated(array, array)
CORE/Cake/Model/Model.php line 2149 → ModelValidator->validateMany(array, array)
CORE/Cake/Model/Model.php line 2082 → Model->validateMany(array, array)
APP/Controller/PeopleController.php line 110 → Model->saveMany(array, array)
[internal function] → PeopleController->add_step(string)
CORE/Cake/Controller/Controller.php line 485 → ReflectionMethod->invokeArgs(PeopleController, array)
CORE/Cake/Routing/Dispatcher.php line 186 → Controller->invokeAction(CakeRequest)
CORE/Cake/Routing/Dispatcher.php line 161 → Dispatcher->_invoke(PeopleController, CakeRequest, CakeResponse)
APP/webroot/index.php line 97 → Dispatcher->dispatch(CakeRequest, CakeResponse)
milesj commented 11 years ago

Mind if I see that step logic code?

jacobroufa commented 11 years ago

For sure. Sorry if I seem obtuse... Not really used to the whole collaborative coding/debugging thing. I'm the only programmer at work and as such I sometimes get caught up in issues that aren't always issues. :)

https://gist.github.com/4558642

add_setup only gets run once, basically to clear the session, init the step loop and direct a user to the first step. add_step takes over. At the top there, I've set up the beginning of a temp-image mover. I'd like to use Transit, if possible because it's already there, and just $file->move(TMPDIR), then set the $currentSessionData['Person']['headshot']['tmp_name'] to that new path. Do you think that might work, and if so how would you suggest implementing? Temp files can get cleaned out regularly with a cron task, so I'm not worried about the extra storage space...

jacobroufa commented 11 years ago

Also, you can see the live form at http://beta.e-rockford.com/apps/gtkm/people/add_setup, with form data pr'ed at the top, if you'd like.

milesj commented 11 years ago

I'm pretty sure that $_FILES data only lasts for the current request and then gets deleted. Perhaps you could move the file upload portion to the last step?

I never tested saveMany() and saveAssociated(). I should test those and make sure that's not the problem either.

jacobroufa commented 11 years ago

I put the upload on the last step and it went through just fine. Not ideal though because of the way the form is layed out, with personal information before the q&a stuff. In any event, that seems to work for now.

saveMany()/saveAssociated() seems to work just fine here though! I didn't expect any issue there, because Cake expects arrays formatted in a certain way to use related models and Uploader formats the array differently. No clashing, as far as I can tell.

Would it make sense to add an extra step to the initial file handling by Uploader, to create a temporary tmp file and abstract that functionality away from PHP? This could allow it to automagically handle multi-step forms and save someone else the trouble.

milesj commented 11 years ago

That wouldn't work as I would need the upload information along with it, like the error state and size. You could always save the query on the first step (which will do the upload) into the database, then use that record ID for the next steps and do an update on it. This will circumvent the single query on the last step and should work.

milesj commented 11 years ago

You could also use the Transit class directly and do the upload on the first step, and then save the path in the session until the last step. More work and it wouldn't use the Attachment, but there is that option.

jacobroufa commented 11 years ago

Gotcha. I may implement the former if I have time.

Thanks again for your diligence! :+1: