how to unit test a controller in laravel? - laravel

This is the test function
public function testCreateSuccess(){
$user=['first_name'=>"heba",'last_name'=>"chakaron",'email'=>"hebachakaron#gmail.com",'password'=> "123456"];
$userReturned=['first_name'=>"heba",'last_name'=>"chakaron",'email'=>"hebachakaron#gmail.com",'password'=> Hash::make('123456')];
$userRequest = UserRegisterRequest::create("http://127.0.0.1:8000/api/users/", 'POST',[
'first_name'=>"heba",
'last_name'=>"chakaron",
'email'=>"hebachakaron#gmail.com",
'password'=>'123456',
'password_confirmation'=>'123456'
]);
/**
* #var UserService|\Mockery\MockInterface|\Mockery\LegacyMockInterface
*/
$mock=Mockery::mock(UserService::class);
$mock->shouldReceive('create')->with($user)
->once()
->andReturn($userReturned);
$this->userController=new UserController($mock);
$this->app->instance('App\Services\UserService', $mock);
$created=$this->userController->create($userRequest);
$this->assertSame($created['password'],$userReturned['password']);
}
This is the controller
class UserController extends Controller
{
//
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
public function create(UserRegisterRequest $request)
{
$data = $this->userService->create($request->validated());
return HTTPResponse::ok($data);
}
}
This is the UserRegisterRequest
class UserRegisterRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'first_name' => 'required|string|max:255',
'last_name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
];
}
}
When I run this testCreateSuccess it return There was 1 error:
Tests\Unit\UserTest::testCreateSuccess
Error: Call to a member function validated() on null
and the error is on the line $created=$this->userController->create($userRequest)

Related

Update a record of DB if it exists after importing a Csv file in Laravel?

I'm using Maatwebsite\Excel to import a csv file to store new users in the DB. If i have already a record in DB with the same data it creates and error. How can i update the record if it exists ?
My import class is :
class UsersImport implements ToModel, WithCustomCsvSettings, WithHeadingRow
{
/**
* #param array $row
*
* #return \Illuminate\Database\Eloquent\Model|null
*/
public function model(array $row)
{
return new User([
'firstname' => $row['firstname'],
'lastname' => $row['lastname'],
'email' => $row['email'],
'password' => $row['pass'],
'created_at' => $row['registered'],
]);
}
public function getCsvSettings(): array
{
return [
'delimiter' => ","
];
}
}
You should implement the WithUpserts interface. Also you need to specify which field should be unique so if it exists, it will update the record, else it will create new one.
Assuming your unique column is email, change your code to the following:
class UsersImport implements ToModel, WithCustomCsvSettings, WithHeadingRow, WithUpserts
{
/**
* #param array $row
*
* #return \Illuminate\Database\Eloquent\Model|null
*/
public function model(array $row)
{
return new User([
'firstname' => $row['firstname'],
'lastname' => $row['lastname'],
'email' => $row['email'],
'password' => $row['pass'],
'created_at' => $row['registered'],
]);
}
public function getCsvSettings(): array
{
return [
'delimiter' => ","
];
}
public function uniqueBy()
{
return 'email';
}
}

How to validate email in custom rule

I use Laravel.
I use the same field for username and email. Additionally the user has to choose, if the input is an username or an email.
If the type is email, i like to add the email validation. If it's username, there is no email validation needed.
I tried to create a custom rule with the if function. But how can i then validate the email?
class StoreUserRequest extends FormRequest
{
public function rules()
{
return [
'first_name' => 'required',
'last_name' => 'required',
'password' => 'required',
'email' => ['required', new ValidateEmailRule()]
];
}
public function authorize()
{
return true;
}
}
class ValidateEmailRule implements Rule
{
/**
* Create a new rule instance.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
if (request()->is_email) {
//validate email ---here i need help to get the right code---
}
return true;
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'The validation error message.';
}
}
Can't you just use 'email' => 'required_if:is_email:1?
EDIT:
$rules = [
'first_name' => 'required',
'last_name' => 'required',
'password' => 'required',
];
if ($this->is_email) {
$rules['email'] = 'email';
}
return $rules;

how to set cakephp 3 email unique validation

I created a table users with 2 fields id(primary),(email)
I want email unique validation used following code but not working only not empty working.
<?php
namespace App\Controller;
use App\Model\Validation\UserValidator;
class UsersController extends AppController
{
public function register()
{
$this->loadModel("users");
if ($this->request->is('post')) {
$validator = new UserValidator();
$errors = $validator->errors($this->request->getData());
if (empty($errors)) {
} else {
$this->set('errors', $errors);
}
}
}
}
src/Model/Validation/UserValidator.php
<?php
namespace App\Model\Validation;
use Cake\Validation\Validator;
use Cake\ORM\Table;
use Cake\ORM\Rule\IsUnique;
class UserValidator extends Validator {
public function __construct()
{
parent::__construct();
$this
->notEmpty('name', 'The name field cannot be left empty')
->notEmpty('email', 'Fill Valid Email Id')
->add('email',['unique' => ['rule' => 'validateUnique', 'provider' => 'table', 'message' => 'Not unique']])
->notEmpty('mobile', 'Fill Valid 10 Digit Mobile No.');
}
}
create this file under \src\Model\Table\Userstable.php
updated
change the the filename capitalize the to make it \src\Model\Table\UsersTable.php
in my code below I validated my username and email as unique
use App\Model\Entity\User;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\ORM\Rule\IsUnique;
/**
* Users Model
*
*/
class UsersTable extends Table
{
/**
* Initialize method
*
* #param array $config The configuration for the Table.
* #return void
*/
public function initialize(array $config)
{
parent::initialize($config);
$this->table('users');
}
/**
* Default validation rules.
*
* #param \Cake\Validation\Validator $validator Validator instance.
* #return \Cake\Validation\Validator
*/
public function validationDefault(Validator $validator)
{
$validator
->requirePresence('username','create')
->notBlank('username', 'A username is required')
->add('username', 'unique', [
'rule' => 'validateUnique',
'provider' => 'table',
'message' => 'Username is already used'
]);
$validator
->requirePresence('email','create')
->notBlank('email', 'An email is required')
->add('email', 'unique', [
'rule' => 'validateUnique',
'provider' => 'table',
'message' => 'Email is already used'
]);
return $validator;
}
public function buildRules(RulesChecker $rules)
{
$rules->add($rules->isUnique(['username']));
$rules->add($rules->isUnique(['email']));
return $rules;
}
}

Authentication failed results in WSOD

I'm using Larave 4.2, and everything works just fine. If I enter the correct credentials I will be taken to the correct URL (the one with auth filter). But the problem I'm currently experiencing is when one of the fields entered is incorrect and the user submits it will show a white screen.
I'm expecting of course that the user will be redirected back to login page with Input and display the error.
I've checked the filters, and quite sure it is still what came with Laravel and didn't change anything.
My routes
<?php
Route::get('login', function()
{
// just a shortcut to redirec to /login into /cms/login : prevents redirect LOOP
return Redirect::route('cms.login');
});
Route::group(array('prefix' => 'cms'), function()
{
Route::get('/', function()
{
if (Auth::guest())
{
return Redirect::route('cms.login');
}
else
{
return Redirect::route('cms.home');
}
});
Route::get('login', array(
'as' => 'cms.login',
'uses' => 'CMSController#login'
));
Route::post('login', array(
'as' => 'cms.postLogin',
'uses' => 'CMSController#userLogin'
));
Route::get('logout', array(
'as' => 'cms.logout',
'uses' => 'CMSController#userLogout'
));
Route::group(array('before' => 'auth'), function()
{
Route::get('home', array(
'as' => 'cms.home',
'uses' => 'CMSController#home'
));
Route::get('my-account', array(
'as' => 'cms.myaccount',
'uses' => 'AccountsController#myAccount'
));
Route::get('my-account/edit', array(
'as' => 'cms.edit-myaccount',
'uses' => 'AccountsController#editMyAccount'
));
Route::resource('accounts', 'AccountsController');
Route::resource('products', 'ProductsController');
Route::resource('news', 'NewsController');
Route::resource('settings', 'SettingsController');
Route::resource('homepage-sliders', 'HomepageSlidersController');
Route::resource('testimonials', 'TestimonialsController');
Route::resource('effects', 'EffectsController');
});
});
User model
<?php
use Illuminate\Auth\UserTrait;
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableTrait;
use Illuminate\Auth\Reminders\RemindableInterface;
class User extends Eloquent implements UserInterface, RemindableInterface {
use UserTrait, RemindableTrait;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
/**
* Fillable array
*
*/
protected $fillable = array('email', 'password', 'username', 'position', 'mobile');
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = array('password', 'remember_token');
/**
* Sets the Validation Rules when Logging In
*
* #var array
*/
public static $loginRules = array(
'email' => 'required|email',
'password' => 'required|alpha_dash|min:6'
);
/**
* Sets the Validation Rules creating a User
*
* #var array
*/
public static $rules = array(
'email' => 'required|email|unique:users',
'username' => 'required|min:2|unique:users',
'position' => 'required|',
'mobile-number' => 'required|numeric|digits:11',
'password' => 'required|alpha_dash|min:6|confirmed',
'password_confirmation' => 'required|alpha_dash|min:6'
);
/**
* Sets the Validation Rules updating a User
*
* #var array
*/
public static $updateRules = array(
'username' => 'required|min:2',
'password' => 'required|alpha_dash|min:6|confirmed',
'password_confirmation' => 'required|alpha_dash|min:6'
);
/**
* Defines many-to-many relationship with Module
*
*/
public function permissions()
{
return $this->belongsToMany('Module', 'permissions')->withPivot('add','edit', 'view','delete');
}
/**
* Get the unique identifier for the user.
*
* #return mixed
*/
public function getAuthIdentifier()
{
return $this->getKey();
}
/**
* Get the password for the user.
*
* #return string
*/
public function getAuthPassword()
{
return $this->password;
}
/**
* Get the e-mail address where password reminders are sent.
*
* #return string
*/
public function getReminderEmail()
{
return $this->email;
}
/**
* Gets the Remember Token
*
* #return string $this->remember_token
*/
public function getRememberToken()
{
return $this->remember_token;
}
/**
* Set the Remember Token
*
* #param string $value
*/
public function setRememberToken($value)
{
$this->remember_token = $value;
}
/**
* Get the Remember Token name
*
* #return string 'remember_token'
*/
public function getRememberTokenName()
{
return 'remember_token';
}
/**
* Get the password and Hash it before saving to the database.
*
* #param string $value
*/
public function setPasswordAttribute($value)
{
$this->attributes['password'] = Hash::make($value);
}
/**
* Checks if Guest User input invalid credentials
*
* #param array $credentials
* #return object $validation
*/
public static function loginIsInvalid($credentials)
{
$validation = Validator::make($credentials, self::$loginRules);
if ($validation->fails())
{
return $validation;
}
}
My CMSController
<?php
class CMSController extends BaseController {
/**
* Display the login page.
* GET /cms
*
* #return Response
*/
public function login()
{
return View::make('cms.login');
}
/**
* Accepts the post request for login
* of user in CMS
*
*/
public function userLogin()
{
$user_credentials['email'] = Input::get('email');
$user_credentials['password'] = Input::get('password');
//sets the remember_me variable
if (Input::has('remember'))
{
$remember_me = true;
}
else
{
$remember_me = false;
}
if ($errors = User::loginIsInvalid($user_credentials))
{
return Redirect::route('cms.login')->withInput()->withErrors($errors);
}
if (Auth::attempt(array(
'email' => $user_credentials['email'],
'password' => $user_credentials['password']), $remember_me))
{
return Redirect::route('cms.home');
}
}
/**
* Accepts the post request for logout
* of user in CMS
*
*/
public function userLogout()
{
Session::clear();
Auth::logout();
return Redirect::route('cms.login');
}
/**
* Directs user to home page
*
*/
public function home()
{
return View::make('cms.home');
}
}
Currently in your code there is nothing after Auth::attempt() - so if the Auth fails - it has no where to go.
Just add a return after the Auth::attempt() to make it work
if (Auth::attempt(array(
'email' => $user_credentials['email'],
'password' => $user_credentials['password']), $remember_me))
{
return Redirect::route('cms.home');
}
return Redirect::route('cms.login')->withInput()->withErrors($errors);

Laravel JsonResource: array_merge_recursive(): Argument #2 is not an array

I have a JsonResource of Post that should return a single post. But after joining some other data I get this error: array_merge_recursive(): Argument #2 is not an array.
This does not work:
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show($slug)
{
// $post = Post::findOrFail($id);
$post = Post::where('slug', $slug)->first();
// return single post as resource
return new PostResource($post);
}
When I directly return $posts, I get a json back, almost fine. But it doesnt contain the joined data comment.
Here is the class Post extends JsonResource.
public function toArray($request)
{
// return parent::toArray($request);
$img = '.'.pathinfo('storage/'.$this->image, PATHINFO_EXTENSION);
$imgName = str_replace($img,'', $this->image);
$img = $imgName.'-cropped'.$img;
return [
'id' => $this->id,
'title' => $this->title,
'body' => $this->body,
'excerpt' => $this->excerpt,
'image' => asset('/storage/' . $this->image),
'image_small' => asset('storage/' . $img),
'author_id' => $this->author_id,
'category_id' => $this->category_id,
'seo_title' => $this->seo_title,
'slug' => $this->slug,
'meta_description' => $this->meta_description,
'meta_keywords' => $this->meta_keywords,
'status' => $this->status,
'featured' => $this->featured,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'user' => User::find($this->author_id),
'commentCount' => $this->comment->where(['status' => 1, 'id_post' => $this->id])->count(),
];
}
// **Big mistake below here**:
public function with($request)
{
// return [
// 'version' => '1.0.0',
// ];
}
Model:
class Post extends Model
{
public $primary_key = 'id';
public $foreign_key = 'id_post';
public function user()
{
return $this->belongsTo('App\User', 'id_author', 'id');
}
public function comment()
{
return $this->belongsTo('App\Comment', 'id', 'id_post');
}
}
Why do I get a warning about array_merge_recursive()?
I wan't reproduce issue with your code, but - are you sure you included everything? Looking at https://laravel.com/docs/5.6/eloquent-resources#writing-resources it's possible to define additional data data will be returned too like this:
/**
* Get additional data that should be returned with the resource array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function with($request)
{
return [
'meta' => [
'key' => 'value',
],
];
}
So I was able to reproduce the issue when I added to this Post resource class the following method:
public function with($request)
{
return 'test';
}
as you see it's returning just string and not array and then I was getting same error as you did.
But when I didn't have this method implemented at all or when I return just an array, everything is fine.
So to sum up - make sure you don't have with method defined that returns something else than array.

Resources