CakePHP 3.x - File validators on upload always failing - file

I am working on an image upload. I am trying to do the image verification in the model (like it should, right?), however I can't get the validation to work.
I put the following in src/Model/Table/CommentTable:
public function validationDefault(Validator $validator)
{
//...
$validator->add('photofile', 'upload', [
'rule' => ['uploadedFile', ['maxSize' => 1048576, 'optional' => true, 'types' => ['image/*']]],
'message' => 'Not a valid file'
]);
//...
}
For testing my controller looks like (I hard-coded an existing file):
$comment = $this->Comments->newEntity([
'body' => $data['body'],
'bin_id' => $bin_id,
'user_id' => $this->Auth->user('id'),
'photofile' => 'C:/Users/Robert/Downloads/test.jpg'
]);
This file is only a couple of bytes, yet after debugging $comment is shows an error in 'photofile':
'[errors]' => [
'photofile' => [
'upload' => 'Not a valid file'
]
],
So why is the validator always failing? Am I using it correctly?

Well, it's not an uploaded file, so that's generally the expected behavior. Also a string isn't supported as a valid value at all, the value must either be a file upload array, or as of CakePHP 3.4, an object that implements \Psr\Http\Message\UploadedFileInterface.
For file upload arrays, the uploadedFile validation rule provided by CakePHP will utilize is_uploaded_file(), which will fail for files that haven't actually been uploaded, and you cannot emulate them.
As of CakePHP 3.4 you can use UploadedFileInterface objects as already mentioned. CakePHP requires \Zend\Diactoros\UploadedFile which provides an implementation that you could utilize for testing purposes, like:
$file = 'C:/Users/Robert/Downloads/test.jpg';
$comment = $this->Comments->newEntity([
'body' => $data['body'],
'bin_id' => $bin_id,
'user_id' => $this->Auth->user('id'),
'photofile' => new \Zend\Diactoros\UploadedFile(
$file,
filesize($file),
\UPLOAD_ERR_OK,
pathinfo($file, \PATHINFO_BASENAME),
'image/jpeg'
)
]);
However while this will pass validation, you won't be able to move that file using UploadedFile::moveTo(), as it uses move_uploaded_file(), which again only works with files that have actually been uploaded.
Also regex validation of MIME types only works when passed as a string (this currently isn't documented), and it must be a valid regex including delimiters, like
'types' => '#^image/.+$#'
Alternatively specify the exact types as an array, ie
'types' => ['image/jpeg', 'image/png', /* ... */]
See also
Zend Diactoros API > UploadedFile
CakePHP API > \Cake\Validation\Validation::uploadedFile()

Related

CakePHP 3.4: set up an email transport only for tests

I'm trying to write tests for an action that sends an email, using get() / posts() methods provided by the IntegrationTestCase class.
The code is something like this:
$this->getMailer('User')
->set('someVarName', 'someVarValue)
->send('forgotPassword', [$user]);
Normally this code works.
But by testing, I get this error:
1) MeCms\Test\TestCase\Controller\UsersControllerTest::testForgotPassword
BadMethodCallException: Cannot send email, transport was not defined. Did you call transport() or define a transport in the set profile?
/home/mirko/Libs/Plugins/MeCms/vendor/cakephp/cakephp/src/Mailer/Email.php:2049
/home/mirko/Libs/Plugins/MeCms/vendor/cakephp/cakephp/src/Mailer/Mailer.php:252
/home/mirko/Libs/Plugins/MeCms/src/Controller/UsersController.php:213
/home/mirko/Libs/Plugins/MeCms/vendor/cakephp/cakephp/src/Controller/Controller.php:440
/home/mirko/Libs/Plugins/MeCms/vendor/cakephp/cakephp/src/Http/ActionDispatcher.php:119
/home/mirko/Libs/Plugins/MeCms/vendor/cakephp/cakephp/src/Http/ActionDispatcher.php:93
/home/mirko/Libs/Plugins/MeCms/vendor/cakephp/cakephp/src/Routing/Dispatcher.php:60
/home/mirko/Libs/Plugins/MeCms/vendor/cakephp/cakephp/src/TestSuite/LegacyRequestDispatcher.php:61
/home/mirko/Libs/Plugins/MeCms/vendor/cakephp/cakephp/src/TestSuite/IntegrationTestCase.php:426
/home/mirko/Libs/Plugins/MeCms/vendor/cakephp/cakephp/src/TestSuite/IntegrationTestCase.php:360
/home/mirko/Libs/Plugins/MeCms/tests/TestCase/Controller/UsersControllerTest.php:345
I've been looking a bit, but I did not understand how to set up a transport only for tests.
Thanks.
I didn’t came across such requirement, but the following should work.
In your /tests/bootstrap.php define a constant, so we can tell if we are in a testing environment:
define('_TEST', true);
// important: define above requiring the /config/bootstrap.php
require dirname(__DIR__) . '/config/bootstrap.php';
In /config/bootstrap.php check against the constant after the default app config file is loaded:
Configure::load('app', 'default', false);
// load an additional config file `/config/app_testing.php` in testing environment
if (defined('_TEST') && _TEST === true) {
Configure::load('app_tests');
}
Finally create the config file /config/app_tests.php for testing and overwrite some of the default config values:
<?php
return [
'Email' => [
'default' => [
'transport' => 'gmail',
'log' => true
]
],
'EmailTransport' => [
'gmail' => [
'host' => 'ssl://smtp.gmail.com',
'port' => 465,
'username' => 'GoogleMailUserName',
'password' => 'GoogleMailPassword',
'className' => 'Smtp'
]
]
];

Model behavior trying to validate when it shouldn’t be

I’ve created a simple model behavior in my CakePHP application to handle file uploads. In its beforeValidate() method I have the following:
public function beforeValidate(Model $Model, $options = array()) {
$maxFileSize = '1MB';
$Model->validator()->add($this->settings[$Model->alias]['field'], array(
'extension' => array(
'rule' => array('extension', $this->settings[$Model->alias]['allowedExtensions']),
'message' => __('Please supply a valid image'),
'allowEmpty' => ($this->settings[$Model->alias]['required'] === false)
),
'fileSize' => array(
'rule' => array('fileSize', '<=', $maxFileSize),
'message' => __('Image must be less than %s', $maxFileSize)
),
'uploadError' => array(
'rule' => 'uploadError',
'message' => __('Something went wrong with the upload')
)
));
return true;
}
I’m dynamically adding file field-related validation. The first rule is checking the extension is in an allowed array of extensions passed in the behavior settings, and I’ve an allowEmpty key that equates to true because a file upload is not mandatory in this case.
This behavior is attached to an Event class. When editing an event, the extension validation rule kicks in, even though I’ve specified allowEmpty as true. Why is this?
Is it because the data passed in the file field is an array and actually equates to non-empty, therefore the validation is kicking in? If so, how can I combat this? I only want validation to kick in if a file has actually been uploaded.
Is it because the data passed in the file field is an array and actually equates to non-empty, therefore the validation is kicking in?
Yes. If no file is uploaded the array key for the model field will still be present, with a none-empty value. I.e. something like:
//$request->data
array(
'Model' => array(
'field' => array(
...
'size' => 0,
'error' => 4
)
)
);
Coping with optional file uploads
To prevent problems validating a field which is a file upload - a simple option is to check for UPLOAD_ERR_NO_FILE and wipe the relevant model data. In a beforeValidate callback that could look like this:
$field = $this->settings[$Model->alias]['field'];
if (
isset($Model->data[$Model->alias][$field]['error']) &&
$Model->data[$Model->alias][$field]['error'] === UPLOAD_ERR_NO_FILE
) {
unset($Model->data[$Model->alias][$field]);
}
Alternatively add a bail-early chunk of code to all validation rules:
function validateExt($Model, ...) {
$field = $this->settings[$Model->alias]['field'];
if ($Model->data[$Model->alias][$field]['error'] === UPLOAD_ERR_NO_FILE) {
return true;
}
...
}
Such that even if called with an empty file upload the validation rules do not return false negatives. Incidentally, validation rule-order matters, it would make more sense to check for an upload error - before validating the contents of the upload =).

Error Loading Views After Changing Route Value of module.config.php - Zend Framework

This question is regarding Zend Framework application version: 2.1.3. I, the developer, am new to Zend Framework and would greatly value your assistance.
I was making a module for a 'Donor Management System' of a church. The module I am working on is called the 'QueryBuilder'. Previous modules were fine and works great. I use the Zend Helper for Sublime Text and it generated some thing similar for the module.config.php
<?php
/**
*
* #package QueryBuilder
*/
return array(
'controllers' => array(
'invokables' => array(
'QueryBuilder\Controller\QueryBuilder' => 'QueryBuilder\Controller\QueryBuilderController',
),
),
// The following section is new and should be added to your file
'router' => array(
'routes' => array(
'querybuilder' => array(
'type' => 'segment',
'options' => array(
'route' => '/querybuilder[/:action][/:id]',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
'controller' => 'QueryBuilder\Controller\QueryBuilder',
'action' => 'index',
//'action' => 'search',
//'action' => 'recent',
),
),
),
),
),
'view_manager' => array(
'template_path_stack' => array(
'querybuilder' => __DIR__ . '/../view',
),
),
);
?>
Determined to have a little bit of fun after making some successful modules, I changed the router as follows.
<?php
/**
*
* #package QueryBuilder
*/
return array(
'controllers' => array(
'invokables' => array(
'QueryBuilder\Controller\QueryBuilder' => 'QueryBuilder\Controller\QueryBuilderController',
),
),
// The following section is new and should be added to your file
'router' => array(
'routes' => array(
'querybuilder' => array(
'type' => 'segment',
'options' => array(
'route' => '/query-builder[/:action][/:id]',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
'controller' => 'QueryBuilder\Controller\QueryBuilder',
'action' => 'index',
//'action' => 'search',
//'action' => 'recent',
),
),
),
),
),
'view_manager' => array(
'template_path_stack' => array(
'querybuilder' => __DIR__ . '/../view',
),
),
);
?>
Note: Only the route value was changed from /querybuilder... to /query-builder.... When I tried to access the route http://konnections/query-builder I got some error but I didn't go through it.
Wondering why I wan't to be a hero, I changed the value back to its defaults. And tried to load http://konnections/querybuilder but it also gave an error.
Zend\View\Renderer\PhpRenderer::render: Unable to render template "query-builder/query-builder/index"; resolver could not resolve to a file
No where in the code of the module you could find the words query-builder. So common sense says there can be no way query-builder/query-builder/index needs to be accessed.
Thinking it might be some caching, I looked for it in the entire application folder. Then I restarted Apache, the computer, deleted and made a new Module with the same name QueryBuilder and still the error is there.
Note: This is a plugin that was done nothing with. I only changed the route value thinking it will make the URL look neater.
Here is what I end up with [Image]:
Nope, this is not IE, because I tried in Chrome (which didn't access the query-builder url) as well.
The whole of Zend Folder (the root of the website) has no reference to query-builder. Where does it come from and how can I change it?
Thanks in advance.
from ZF1 series (http://framework.zend.com/manual/1.12/en/zend.controller.basics.html), ZF2 is the same but not found this description on manual.
Case Naming Conventions
Since humans are notoriously inconsistent at maintaining case sensitivity when typing links, Zend Framework actually normalizes path information to lowercase. This, of course, will affect how you name your controller and actions... or refer to them in links.
If you wish to have your controller class or action method name have multiple MixedCasedWords or camelCasedWords, you will need to separate those words on the url with either a '-' or '.' (though you can configure the character used).
As an example, if you were going to the action in FooBarController::bazBatAction(), you'd refer to it on the url as /foo-bar/baz-bat or /foo.bar/baz.bat.
I am Ziyan, the one who asked the question in the first place.
After not finding a solution in Google, I wondered in to making another module and ended up with the same plight. Confused I made a new installation of Zend Framework and made a module with the same name.
Zend\View\Renderer\PhpRenderer::render: Unable to render template "query-builder/query-builder/index"; resolver could not resolve to a file did not go away.
Knowing it is not some thing like reconfiguration, I looked closer.
It seemed to put a dash between every word. I was like Zend is insane! But no, it seems to be splitting the Module name from each upper case letter and adjoining them using a '-' (dash).
In my case QueryBuilder becomes query-builder or plugin named HelloModules would be hello-modules.
So the view manager seems to be looking for ../views/query-builder/query-builder/index.
In my case, ZF Helper for Sublime text needs to look in to the situation. If you could fix the plugin at GitHub it would be great. I will give a try my self.
I couldn't find any proof for my claiming and no time to go through the source codes. Would be glad if some one provide some links.

The requested URL /drupalhr/drupal/employee/add/ was not found on this server

I have created a custom module in drupal with entities. I have installed the entity api module. I have created my database schema with just two columns (employee_id, first_name) through the help of employee_management.install file (where as employee_management is my custom module name) and employee is my entity name.
I have also written the requisite functions employee_management.module but still it shows me the error , Whenever i tried to add a new entity in the admin/structure/employee it shows me the following error: "Not Found".
The requested URL drupal/employee/add/ was not found on this server.
function employee_management_entity_info() {
$employee_info['employee'] = array(
// A human readable label to identify our entity.
'label' => t('Employee Entity'),
// The controller for our Entity - extends the Drupal core controller.
'controller class' => 'EmployeeController',
// The table defined in hook_schema()
'base table' => 'employee',
// Returns the uri elements of an entity
'uri callback' => 'employee',
// Fieldable that we can attach fields to it - the core functionality will
// do the heavy lifting here.
'fieldable' => TRUE,
// The unique key of our base table.
'entity keys' => array(
'id' => 'employee_id',
),
// FALSE disables caching - caching functionality is handled by Drupal core
'static cache' => TRUE,
// Attach bundles - i.e. alternative configurations of fields associated with a main entity.
'bundles' => array(
'employee' => array(
'label' => 'Employee',
// Information below is used by the Field UI - they "attach" themselves here and lets us
// do the standard field management that all the core entities enjoy.
'admin' => array(
'path' => 'admin/structure/employee/add',
'access arguments' => array('administer employee entities'),
),
),
),
// View modes allow entities to be displayed differently based on context. We simply have one option
// here but an alternative would be to have a Full and Teaser mode akin to node.
'view modes' => array(
'full' => array(
'label' => t('Full'),
'custom settings' => FALSE,
),
)
);
return $employee_info;
}
EDIT
function employee_uri($employee) {
return array(
'path' => 'employee/' . $employee->employee_id,
);
}
And here is the complete list of function in the file employee_management.module
You don't automagically get the route and form to create your entity, you'll have to implement that yourself. See hook_menu and this guide.

Cakephp validating specific field with specific rule without saving the data from controller

I want to validate fields with Cakephp model validation, without saving the data, for which i am using the following code in controller.
$this->Model->set($this->data);
if ($this->Model->validates()) {
......
}
But here i want to validate only some specific field like 'email_field' and one of its rule 'email'. In model i have specified some other rules for 'email_field' like 'unique' and 'notempty' but i don't want to validate those rules.
How can it be achieved ?
The above will work definitely but it's not an elegant solution when cake has already documented how to validate specific fields of a Model.
if ($this->Model->validates(array('fieldList' => array('field1', 'field2')))) {
// valid
} else {
// invalid
}
For more detail see cookbook
you have different options
you can dynamically unset those other rules:
unset($this->Model->validate['field']['someRuleName']);
or you can assign a completely new rule set to this field
or you can use a different "nonexistent" field for this validation, e.g. "some_other_field" with special rules.
......
$this->Registry->validate = array(
'email_field' => array(
'between' => array(
'rule' => array('between', 10, 100),
'message' => 'Your custom message here',
'required' => true,
),
)
);
$this->Model->set($this->data);
if ($this->Model->validates(array('fieldList' => array('email_field')))) {
......
}

Resources