Using CakePHP 2.4.9 on a LAMP setup (Ubuntu 13.10, Apache 2, MySQL Server 5.5, PHP 5.5.3).
Trying to package a model (User) into a plugin but I have run into a strange problem:
In two different actions I use save() to either create or update a User. This works:
if ($this->request->data) {
if ($this->User->create() && $this->User->save($this->request->data)) {
$this->Session->setFlash(_('<strong>Congratulations!</strong> Successfully created a new user.'), 'default', array('class' => 'alert alert-success'));
$this->redirect(array('action' => 'register'));
} else {
$this->Session->setFlash(_('<strong>Oops!</strong> Could not create a new user.'), 'default', array('class' => 'alert alert-danger'));
}
}
But this doesn't work:
if ($this->request->data) {
$this->User->id = $id;
if ($this->User->save($this->request->data)) {
$this->Session->setFlash(_('<strong>Congratulations!</strong> Successfully updated your user account.'), 'default', array('class' => 'alert alert-success'));
$this->redirect(array('action' => 'settings'));
} else {
$this->Session->setFlash(_('<strong>Oops!</strong> Could not update user account.'), 'default', array('class' => 'alert alert-danger'));
}
}
When I try to save the latter example (the "update action") it gives me the following error:
Fatal Error
Error: Call to a member function getColumnType() on a non-object
File: /home/johan/sites/userplugin/lib/Cake/Model/Model.php
Line: 1412
Notice: If you want to customize this error message, create app/View/Errors/fatal_error.ctp
The forms are pretty standard, I use the FormHelper to create form and fields. But in the update form I have a hidden field with the ID:
<?php echo $this->Form->hidden('id'); ?>
If I remove that, the update form goes through but creates a new object! So it's like $this->User->id = $id doesn't do anything. I currently set $id "manually", so $id = 1...
I tried searching for similar issues but didn't find anything. One discussion mentioned the ID field, maybe it's not correctly set up? But I couldn't find any solution in regards to that.
There's a bad model reference
The different code permutations are not directly related to the problem - or may simply indicate another different problem =).
The line of code that is failing is this:
$this->{$model}->getColumnType($column);
Where $this is the User model instance. What that probably means is that when it fails, the object is not the class you expect to check simply do:
debug(get_class($this->User));
In your controller, it is most likely AppModel.
What causes this
A typical cause of that kind of "sometimes it works, sometimes it doesn't" problem is that there are some references to the model using the plugin prefix, and there are some that don't i.e.
<?php
App::uses('MyPluginAppModel', 'MyPlugin.Model');
class Group extends MyPluginAppModel {
public $hasMany = array(
'User' => array(
'className' => 'User'
),
#'User' # Or like this
)
}
Whereas it should be like this:
<?php
App::uses('MyPluginAppModel', 'MyPlugin.Model');
class Group extends MyPluginAppModel {
public $hasMany = array(
'User' => array(
'className' => 'MyPlugin.User'
),
#'MyPlugin.User' # Or like this
)
}
What happens here is it depends upon how the relevant model is first accessed, as to what populates the User key in the class registry and is returned for all subsequent requests for that model reference.
To fix the problem, ensure that the model is always referenced with the correct plugin prefix (or of course no prefix if it's not a plugin model).
What I ultimately found out (and should've ruled out earlier, really) was that having an array defined in my AppModel, for configuring the plugin, named the same as my plugin was stupid, borderline retarded perhaps.
I.e. if your plugin is called MyPlugin, do not define a $myplugin array in your AppModel.
try this
//Controller
public function edit(){
if (!empty($this->request->data)) {
//if you want to save on User object
$this->User->id = $this->request->data['User']['id'];
if ($this->User->save($this->request->data)) {
.................................
$this->Session->setFlash(_('<strong>Congratulations!</strong> Successfully updated your user account.'), 'default', array('class' => 'alert alert-success'));
$this->redirect(array('action' => 'settings'));
} else {
$this->Session->setFlash(_('<strong>Oops!</strong> Could not update user account.'), 'default', array('class' => 'alert alert-danger'));
}
} else {
$this->request->data = $this->User->read(null, $id);
}
}
//edit view
<?php
echo $this->Form->create('User');
echo $this->Form->hidden('id');
echo $this->Form->input('first_name');
..
..
.
echo $this->Form->end(__('Save'));
?>
Related
I would like to write a test for my CommentObserver. This observer is only registered in the NovaServiceProvider but not the AppServiceProvider. This means I cannot test my observer by using my own Controllers.
In my eyes I have 3 ways to test my observer:
Either performing a feature test by sending a post request to the Nova API
Mocking the observer by calling the function in the observer to check if the function perfoms as desired
Trying to register my observer on the fly in the AppServiceProvider, performing a request and deregistering the observer in the AppServiceProvider again.
I tried to find a solution for any of these 3 ways to test my observer but unfortunately I faild with any of them.
Problems:
For way 1 I always get a validation error and Nova tells me that my input is invalid.
For way 2 I fail at mocking the observer function
For way 3 I didn't find any solution on how to register and deregister the oberserver on the fly at the AppServiceProvider
Do you guys have idea and solition on how I can test my CommentObserver (which is as written above only registered in my NovaServiceProvider).
Update:
So, here is the code of my observer. I need to have an valid request to test my observer in order to have the ability to access the $request->input('images') variable. I do know I can also use $comment->content instead of request()->input('content') because $comment->content already contains the new content which is not saved it this point.
The reason why I need a valid request is that the variable images is not part of the Comment model. So I cannot use $comment->images because it simply doesn't exist. That's why I need to access the request input. What my observer is basically doing is to extract the base64 images from the content, saves them to the server and replaces them by an image link.
class CommentObserver
{
public function updating(Comment $comment)
{
if (!request()->input('content')) {
return;
}
if (request()->input('content') == $comment->getRawOriginal('content')) {
return;
}
$images = request()->input('images');
if(!is_array($images)) {
$images = json_decode(request()->input('images'));
}
checkExistingImagesAndDeleteWhenNotFound($comment, request()->input('content'), 'comments', 'medium');
$comment->content = addBase64ImagesToModelFromContent($comment, request()->input('content'), $images, 'comments', 'medium');
}
}
This is my test so far. I choose way 1 but as described already this always leads to an validation error by the nova controller and I cannot figure out what is the error/what is missing or wrong.
class CommentObserverTest extends TestCase
{
/** #test */
public function it_test()
{
$user = User::factory()->create([
'role_id' => Role::getIdByName('admin')
]);
$product = Product::factory()->create();
$comment = Comment::factory()->create(['user_id' => $user->id, 'content' => '<p>Das ist wirklich ein super Preis!</p>', 'commentable_type' => 'App\Models\Product', 'commentable_id' => $product->id]);
$data = [
'content' => '<p>Das ist wirklich ein HAMMER Preis!</p>',
'contentDraftId' => '278350e2-1b6b-4009-b4a5-05b92aedaae6',
'pageStatus' => PageStatus::getIdByStatus('publish'),
'pageStatus_trashed' => false,
'commentable' => $product->id,
'commentable_type' => 'App\Models\Product',
'commentable_trashed' => false,
'user' => $user->id,
'user_trashed' => false,
'_method' => 'PUT',
'_retrieved_at' => now()
];
$this->actingAs($user);
$response = $this->put('http://nova.mywebsiteproject.test/nova-api/comments/' . $comment->id, $data);
dd($response->decodeResponseJson());
$das = new CommentObserver();
}
}
Kind regards and thank you
Why depend on the boot method in your NovaServiceProvider? It is possible to call the observe() method on the fly in your test:
class ExampleTest extends TestCase
{
/** #test */
public function observe_test()
{
Model::observe(ModelObserver::class);
// If you need the request helper, you can add input like so:
request()->merge([
'content' => 'test'
]);
// Fire model event by updating model
$model->update([
'someField' => 'someValue',
]);
// Updating should be triggered in ModelObserver
}
}
It should be now be possible in your observer class:
public function updating(Model $model)
{
dd(request()->input('content')); // returns 'test'
}
this is the entry for auth_itemi'm trying to use rbac in yii2-basic framework.
code is as follow:
config/web.php:
'authManager' => [
'class' => 'yii\rbac\DbManager',
],
controller:
public function actionCreate()
{
if(Yii::$app->user->can('countries/create')){
$chk = 'Can Do';
}else{
$chk = 'Can Not Do';
}
echo $chk;exit();
}
Make sure to check that all the points below are working as expected.
The information comes from the Yii2 guide pages and it is explained in more detail there.
Configure the application to use RBAC with the data stored on the database.
'authManager' => [
'class' => 'yii\rbac\DbManager',
],
Add and assign roles and permissions to the database.
One way, probably the simplest, is to use a console controller, but then you have to deal with permissions on user creation and update somewhere else in your code.
<?php
namespace app\commands;
use Yii;
use yii\console\Controller;
class RbacController extends Controller
{
public function actionInit()
{
$auth = Yii::$app->authManager;
$auth->removeAll();
// add "create country" permission better remove '/'
$createCountry = $auth->createPermission('createCountry');
$createCountry->description = 'Create a new country';
$auth->add($createCountry);
// add "admin" role and give this role the "createCountry" permission
$adminRole = $auth->createRole('admin');
$auth->add($adminRole);
$auth->addChild($adminRole, $createCountry);
// Assign roles to user by id, make sure this is the user that you
// are using when testing
$auth->assign($adminRole, 1);
}
}
Add some logs in your controller to check what is not working as expected.
public function actionCreate()
{
// Logs just to find what is wrong, remove them later
if (($user = Yii::$app->user->identity) === null) {
Yii::debug('No user logged in, the problem is there', __METHOD__);
} else {
Yii::debug("User $user->id logged in", __METHOD__);
if (!Yii::$app->user->can('createCountry') {
Yii::debug('User cannot create country', __METHOD__);
if (!Yii::$app->user->can('admin') {
Yii::debug('User does not have admin role', __METHOD__);
} else {
Yii::debug('Admin role does not have createCountry child', __METHOD__);
}
} else {
Yii::debug('User can create country, ALL DONE!', __METHOD__);
}
}
// Remove above this line after finding the problem
// You would keep the logic below this line after finding the problem
if(!Yii::$app->user->can('createCountry')) {
throw new ForbiddenHttpException('You are not allowed to do that');
}
// No 'else' after throwing, more readable code
// Your logic goes here, the user can create countries
}
I'm not sure, but I think:
Your code is almost ok
Missing addChild($adminRole, $createCountry);
The permission is checked against the table auth_item_child.
Parent Child
admin 'createCountry'
admin 'deleteCountry'
guest 'indexCountry'
....
Then you can
if(Yii::$app->user->can('createCountry')){
I created new resources with this code:
class WebserviceRequest extends WebserviceRequestCore {
public static function getResources(){
$resources = parent::getResources();
// if you do not have class for your table
$resources['test'] = array('description' => 'Manage My API', 'specific_management' => true);
$resources['categoryecommerce'] = array('description' => 'o jacie marcin', 'class' => 'CategoryEcommerce');
$mp_resource = Hook::exec('addMobikulResources', array('resources' => $resources), null, true, false);
if (is_array($mp_resource) && count($mp_resource)) {
foreach ($mp_resource as $new_resources) {
if (is_array($new_resources) && count($new_resources)) {
$resources = array_merge($resources, $new_resources);
}
}
}
ksort($resources);
return $resources;
}
}
And new class:
class CategoryEcommerceCore extends ObjectModelCore {
public $category_id;
public $category_core_id;
public static $definition = array(
'table' => "category_ecommerce",
'primary' => 'category_id',
'fields' => array(
'category_core_id' => array('type' => self::TYPE_INT),
)
);
protected $webserviceParameters = array();
}
Webservice is override properly. My class WebserviceRequest is copying to
/override/classes/webservice/WebserviceRequest
but class isn't copying to /override/classes/ when i installing my module.
How to add new resourcess with own logic ? I want to add categories within relation to my table.
Regards
Martin
As soon as there is literally nothing regarding the API except Webkul tutorial... I tried to implement the "Webkul's" tutorial, but also failed. However seems that it's better to use hooks instead of overrides. I used my "reverse engineering skills" to determine the way to create that API, so-o-o-o, BEHOLD! :D
Let's assume you have a custom PrestaShop 1.7 module. Your file is mymodule.php and here are several steps.
This is an install method wich allows you to register the hook within database (you can uninstall and reinstall the module for this method to be executed):
public function install() {
parent::install();
$this->registerHook('addWebserviceResources');
return true;
}
Add the hook listener:
public function hookAddWebserviceResources($resources) {
$added_resources['test'] = [
'description' => 'Test',
'specific_management' => true,
];
return $added_resources;
}
That specific_management option shows you are going to use WebsiteSpecificManagement file instead of database model file.
Create WebsiteSpecificManagement file, called WebsiteSpecificManagementTest (Test - is CamelCased name of your endpoint). You can take the skeleton for this file from /classes/webservice/WebserviceSpecificManagementSearch.php. Remove everything except:
setObjectOutput
setWsObject
getWsObject
getObjectOutput
setUrlSegment
getUrlSegment
getContent (should return $this->output; and nothing more)
manage - you should rewrite it to return/process the data you want.
Add
include_once(_PS_MODULE_DIR_.'YOURMODULENAME/classes/WebserviceSpecificManagementTest.php');
to your module file (haven't figured out how to include automatically).
Go to /Backoffice/index.php?controller=AdminWebservice and setup the new "Auth" key for your application, selecting the test endpoint from the permissions list. Remember the key.
Visit /api/test?ws_key=YOUR_KEY_GENERATED_ON_STEP_4 and see the XML response.
Add &output_format=JSON to your URL to see the response in JSON.
You have to use something like $this->output = json_encode(['blah' => 'world']) within manage method at WebsiteSpecificManagementTest.
i'm currently writing a Application based on YII.
My action for index:
public function actionIndex() {
$data = array();
$data['server'] = Server::model()->findByPk(1);
$data['dataProvider'] = new CActiveDataProvider('ServerUserPermission', array('criteria' => array('condition' => 'serverID=:id', 'params' => array(':id' => 1))));
$this->render('index', $data);
}
my ajax action:
public function actionAddPermission($server) {
if(Util::checkServerPower($server, Permission::MODIFY_SERVER)) {
$perm = new ServerUserPermission;
$perm->userID = 1;
$perm->serverID = $server;
$perm->power = 10;
try {
if ($perm->save()) {
echo "OK";
} else {
echo Util::print_r($perm->getErrors());
}
} catch (Exception $e) {
echo 'Critical Error Code: ' . $e->getCode();
}
} else {
echo 'No Permissions';
}
}
My view links to the addPermission action by using a button:
echo CHtml::ajaxButton("Insert New Player", array('addPermission', 'server' => $server->serverID), array('success'=>'refresh'));
My function Util::checkServerPower(...) checks the current User of the Application. Consequence: Ajax requests in YII are handled by an Guest AuthWeb User, but i need to check whether the User is actually allowed to add permissions or not. I currently cannot think of a secured solution to protect malicious data send by other guests or not. Is it somehow possible to get the (server-side) userID of the Ajax-call?
Thanks anyway
sincerly
I would do it by using the built in access control and extending CWebUser.
It might seem lengthy but I think it's a clean solution. (We already have Yii::app()->user->isGuest and the like, so why not check all permissions here?)
1) Activate the access control filter.
(In one controller or in /components/controller.php for all your controllers at once)
public function filters()
{
return array( 'accessControl' ); // Tell Yii to use access rules for this controller
}
2) Add an access rule
In the concerned controller. (Sorry, I didn't bother with your index-action.)
public function accessRules()
{
return array(
[
'allow',
'actions'=>['AddPermission'],
'expression'=>'$user->has(Permission::MODIFY_SERVER)'
],
['deny'], // Deny everything else.
);
}
3) Extend CWebUser
// components/WebUser.php
class WebUser extends CWebUser {
public function has( $permission)
{
// Check database for permissions using Yii::app()->user->id
...
}
}
4) Configure your app to use your new WebUser instead of CWebUser
// config/main.php
'components'=>[
'user'=>[
'class' => 'WebUser',
],
],
Im new to cakePHP.I just learning the way to validate the data before storing into the database.I just did the following validation to check the birth date given by the user.
class User extends AppModel {
public $name = "Users";
public $validate = array (
'birth_dt' => array ( 'rule' => 'date',
'required' => true,
'allowEmpty' => false,
'message' => 'Please Give valid date' ));
}
I expected a error message when I give wrong data in the birth_dt field.But there is no errors in case of wrong data ,bug the data is not getting store in the database.If I give the valid data also the data is not getting stored.If I remove the validation part then data getting stored in the database.
It not only happen for date ,it happen for other fields like alphaNumeric ,email which I used in my form.Any one please tell what I did wrong.
This is my controller code,
<?php
class UsersController extends AppController{
var $name = 'Users';
var $uses = array('User','Dob');
var $helpers = array('Form');
function index() {
if ($this->request->is('post')) {
if ($this->request->data) {
$this->User->save ( $this->request->data );
$this->Session->setFlash("User Added!");
$this->redirect('/users');
}
}
}
There can be multiple possible issue.Try following.
Before saving user Create new record like this->User->create();
You don't have else part. Write like following in else & check,
else {
$this->Session->setFlash(__('User could not be saved',true));
}
You can check why save() failed by examining the validationErrors array in the User model. From the UsersController it can be access via $this->User->validationErrors.
Furthermore, your controller logic isn't quite right. A was mentioned in some of the comments, you don't check if save() was successful and you always state that a user was added, even when it's not true.
Try something like this:
if($this->request->is('post') && !empty($this->data)) {
if($this->User->save($this->data)) {
$this->Session->setFlash('User added!', 'flash_good');
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash('Could not add player.', 'flash_bad');
}
}
This way, when save() fails for some reason, the user will remain in that action and the form will still be populated with the data the user entered. Furthermore, if you use $this->Form->input(...) to create you form elements the validation errors will be added to the form automagically.
I think your public $validate is wrongly written
Try this
class User extends AppModel {
public $name = "Users";
//For more strict validation you should have more than one parameter
//submitted to the rule array for date which requires the user to enter
// exactly what you need. for example 01/01/12 instead of 01/01/2012
//Please check the following address
//http://book.cakephp.org/2.0/en/models/data-validation.html#Validation::date
//However, this should work fine.
public $validate = array (
'birth_dt' => array (
'rule' => array('date'),
'required' => true,
'allowEmpty' => false,
'message' => 'Please Give valid date'
)
);
}
I think best way for validation is to integrate server side and client side validation.
form fields should be validated on focus out/blur with ajax call to server side validation rules via your controllers. This will ensure consistency in server side and client side validation.
bla..bla :)
I think validate error message not show
because you use $this->redirect('/users');
when redirect cakephp will be render view that clear message error :)
You can comment line //$this->redirect('/users'); and then try again :)