Testing eloquent model events - php

I'm currently using a package that logs activity via listening for model events based on changes made to them (create/update/delete). The code functions as expected locally though when it comes to writing feature tests, logging deletions fails. Here's the code:
it('should create a log when an admin is deleted', function() {
$administrator = Administrator::where('first_name', 'First Name')->first();
$administrator->delete();
//this passes
$this->assertSoftDeleted('administrators', [
'first_name' => $administrator->first_name,
'last_name' => $administrator->last_name,
'email' => $administrator->email,
]);
//this fails
$this->assertDatabaseHas('activity_log', [
'id' => 1,
'description' => 'admin deleted',
'subject_type' => 'App\\Models\\Administrator',
'subject_id' => $administrator->id,
'causer_type' => 'App\\Models\\Administrator',
'event' => 'deleted'
]);
In a create/update test, the second assertion passes because an activity_log is correctly created though on deletion tests, no log is created. The logging code for create/update is in their controllers/actions though for deletion, it's in the models. Is there a way for me to test that model events are correctly being fired correctly, if at all?

Related

laravel validator for rest api

I am trying to follow best practice for REST api on CRUD.
GET: users/ -> all
GET: users/:id -> one specific user.
POST: users/ -> add one
PUT: users/:id -> update specific user.
DELETE: users/:id -> delete one user.
On laravel 8 I want to validate the url :id using the validator, so I have like this on delete user:
$validator = Validator::make(['id' => $request->id], [
'id' => 'exists:users,id,deleted_at,NULL',
]);
And this way to update a user:
$validator = Validator::make(array_merge($request->all(), ['id' => $request->id]), [
'id' => 'required|exists:users,id,deleted_at,NULL',
'name' => 'required',
'surname' => 'required',
'email' => 'required|email:rfc,dns'
]);
As you can see I have to put the id on an array and/or merge with the $request->all().
There is any way in laravel to do this with the request?
I have found 3 ways by Laravel:
$request->add(['variable' => 'value']);
$request->merge(["key"=>"value"]);
$request->request->set(key, value);
But a solution for adding route params to the request before hitting the controller method would be even great.
You can update the request object on the fly and add the ID field, before you validate it, something like
$request['id'] = $id;
// note: the $id is your function's parameter name
$validator = Validator::make(array_merge($request->all()), [
'id' => 'required|exists:users,id,deleted_at,NULL',
'name' => 'required',
'surname' => 'required',
'email' => 'required|email:rfc,dns'
]);
You can do it like you are doing, but doing it with route model binding would be the way to go.
Now when you want to update a user by sending a PUT to /users/:id, and the user does not exist you will get a 422. But what you really want would be a 404.
With route model binding, Laravel will check if the model exists for you and abort with a 404 when it does not.
If route model binding is not an option, you can also just not validate the id with the validator and retrieve the user with User::findOrFail($request->input('id')), the framework will then still abort with a 404 if it can't be found.

My Laravel test is returning a 302 redirect rather than a 403 forbidden status

I have a Model called Type with a title field and a pretty_slug field.
I have a test that is checking that a user can not update a Type instance:
<?
public function test_user_cannot_put_update_page() {
$type = Type::factory()->make([
'title' => 'Original type',
]);
$type->save();
$response = $this->put(route('types.update', [
'pretty_slug' => $type->pretty_slug,
'title' => 'New type',
]));
$response->assertForbidden();
$this->assertDatabaseHas('types', [
'title' => 'Original type'
]);
}
If I do dd($response->getContent()); I can see that a redirect is happening:
Now the weird thing is that I have the exact same for another Model called Level:
<?
public function test_user_cannot_put_update_page() {
$level = Level::factory()->make([
'title' => 'Original level',
]);
$level->save();
$response = $this->put(route('levels.update', [
'pretty_slug' => $level->pretty_slug,
'title' => 'New level',
]));
$response->assertForbidden();
$this->assertDatabaseHas('levels', [
'title' => 'Original level'
]);
}
The Model Level is exactly the same as Type: the same Controller, the same Trait shared, same Policy, same Tests, same routes… I have other Models called Idea, Concept and Episode that have the exact same behavior.
All tests pass, except for my Type Model:
I have no idea why this particular types.update route is not working. It should return a 302 but is instead redirecting.
It is working when I use the webform: the Type instance updates correctly. But the test is failing.
How can I debug this test? Where do I look for an issue in my code?
Thanks for any help.
EDIT 1: added controller and routes
I think the problem is the 'levels.update' route might be protected by the auth middleware.
Since there is no user logged in, the auth middleware will attempt to redirect to the login page instead.
I like IGP's answer. But if that's not it, you might want to check your host configs. Apache or Nginx, or whatever you're using. It could be the route is being called via http and your server is redirecting to https, or visa versa.
Thanks to #Aless55, I found the issue: it was the validation of my Type model that was preventing me from updating the instance.
I looked into the StoreType file, in which I had:
'order' => 'required|numeric',
This means the order field is required. But when I tried calling the types.update route, I wasn't including that field.
One solution would have been to make that field optional. But I ended up including the order field in my test:
$response = $this->put(route('types.update', [
'pretty_slug' => $type->pretty_slug,
'title' => 'Alex new type',
'order' => 1,
]));

How to save multiple values from select2_from_ajax_multiple in Backpack laravel

everyone
I am tring to save the Multiple values from select option, but i don't have knowledge about how to save in laravel Backpack .I have been trying to solve the error from 2 days, but still it's not working from my side.
Please have a look, help would be appreciated.
Here is Screen shot of multiple select option ---
But it does not saving in db, but if i am going to edit the record , it gives this error ---
Here is my controller code
CRUD::addField([ // select2_from_ajax: 1-n relationship
'label' => "City Name", // Table column heading
'type' => 'select2_from_ajax_multiple',
'name' => 'location_id', // the column that contains the ID of that connected entity;
'entity' => 'location', // the method that defines the relationship in your Model
'attribute' => 'name', // foreign key attribute that is shown to user
'tab' => 'Texts',
'data_source' => url('api/location'), // url to controller search function (with /{id} should return model)
'placeholder' => 'Select location', // placeholder for the select
'include_all_form_fields' => true, //sends the other form fields along with the request so it can be filtered.
'minimum_input_length' => 0, // minimum characters to type before querying results
'dependencies' => ['state'], // when a dependency changes, this select2 is reset to null
// 'method' => 'GET', // optional - HTTP method to use for the AJAX call (GET, POST)
"allows_multiple" => true,
'pivot' => true,
]);

Multiple Login table in yii2 IdentityInterface error while login

I have 2 different tables user and organiser and i am trying to create 2 different login for both users.
I am able to sign them up easily and get the record in database but after saving the record i get the error on following code line
if ($user = $model->signup()) {
if (Yii::$app->getUser()->login($user)) { //yii\web\IdentityInterface error
return $this->goHome();
}
}
Following is my configuration module
'components' => [
'user' => [
'identityClass' => 'common\models\User',
'enableAutoLogin' => true,
'identityCookie' => [
'name' => '_frontendOrganiser', // unique for frontend
],
],
'users' => [
'class' => 'yii\web\User',
'identityClass' => 'common\models\Users',
'enableAutoLogin' => false,
'enableSession' => true,
'identityCookie' => [
'name' => '_frontendUser', // unique for frontend
],
],
'session' => [
'name' => 'PHPFRONTSESSID',
'savePath' => sys_get_temp_dir(),
],
]
So what is wrong am i doing here? Do i need to create any other Interface or something or provide different interface for different module?
And i had to do this because organiser table uses password_hash technique to log in the users where my user table is from another project and they uses md5 technique, so i had to create separate module for both users.
Argument 1 passed to yii\web\User::login() must be an instance of yii\web\IdentityInterface, instance of common\models\Users given, called in C:\xampp\htdocs\project\frontend\controllers\SiteController.php on line 202 and defined
The exact error statement is as above.
I think your user model don't implement the Identity interface correctly.
Try check you data model also (in your DB) this must contain all the field managed bay the interface.
And be sure you User implement the Identity Interface correctly.
and mapping the interface method with your model correctly..
See the interface doc for this http://www.yiiframework.com/doc-2.0/yii-web-identityinterface.html

Validate field in model with no table (CakePHP)

I've got a model in CakePHP that doesn't have a table, called Upload. I've got a validation in this Model for a field called source_id.
I've got a form that builds a nice looking $this-data, giving me a well formated set, including:
$this->data['Upload']['source_id']
However, the validation rule I have set doesn't seem to run at all. I copied this validation rule from another model where it does work, so I'm confident that it works:
var $validate = array(
'source_id' => array(
rule' => 'numeric',
'required' => true,
'allowEmpty' => false,
'message' => 'Error!.'
)
);
Can you not validate fields for a model that lacks a database table?
The form uses the Upload model, and submits to another controller action method.
CakePHP 1.2, PHP/MySQL 5, XAMPP.
I'm dumb. You have to trigger a validation check, either with a save() or
$this->Upload->set($this->data);
$this->Upload->validates();
Working now.
You can also fake the database structure by setting the $_schema array, like so:
var $useTable = false;
var $_schema = array(
'name' =>array('type'=>'string', 'length'=>100),
'email' =>array('type'=>'string', 'length'=>255),
'phone' =>array('type'=>'string', 'length'=>20),
'subject' =>array('type'=>'string', 'length'=>255),
'message' =>array('type'=>'text')
);

Categories