Laravel 5.1 - Can't access Auth:user()->data from my controllers - php

I've been having a little trouble with Auth data in Laravel 5.1
I want to get the id of the Logged user and It works if I use it in a View, lets say:
<?php echo Auth::user()->id;?>
But if I try to access to it in a Controller it dowsnt work, and I get this error:
ErrorException in Con_Mascota.php line 101:
Trying to get property of non-object
In line 101 i've:
$cod_usu=$request->user()->cod_usu;
I changed that from $cod_usu=Auth::user()->cod_usu; cause that didnt work either.
Im also including it in the Controller:
use Auth;
Any help is much appreciated :)
I believe the problem is my Auth session is login out after refresh or after i go to another route:
My login funcions is:
*/
public function ini_ses(Request $datos)
{
//Inicia sesion
// Session::put('ses_correo', Input::get('email'));
$correo = $datos->input('email');
$password= $datos->input('password');
if(Auth::attempt(['correo_elec'=>$correo, 'password'=>$password]))
{
$_session['correo']=$correo;
$_session['contra']=$password;
if(Auth::user()->tipo==0)
{
return view('cliente');
}
elseif(Auth::user()->tipo==1)
{
return view('veterinario');
}
elseif(Auth::user()->tipo==2)
{
echo("Admin");
}
}
else
{
var_dump($correo, $password);
}
}
Model:
namespace App;
use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class Usuario extends Model implements AuthenticatableContract,
AuthorizableContract,
CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword;
public $timestamps = false;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'usuario';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['cod_usu', 'correo_elec', 'nombre', 'ape_p', 'ape_m', 'telefono', 'celular', 'pais' , 'tipo'];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = ['contra', 'remember_token'];
}
I found the solution, i changed this:
if(Auth::attempt(['correo_elec'=>$correo, 'password'=>$password]))
to this
if(Auth::attempt(['correo_elec'=>$correo, 'password'=>$password], true))
that eventyally made me have to change my cod_usu in the table for id, and reading jedrzej.kurylo's answer i guess that did the trick

When you log in, Laravel tries to save your user identifier in the session so that it can load it during subsequent requests. By default, model's ID field is called id, which is not true in your case. Therefore, after you authenticate, Laravel stores null as your user's ID and that's the reason it cannot load it later.
You need to tell Eloquent what is the name of your identifier column. You can do it by setting $primaryKey property of your Usuario class:
protected $primaryKey = 'cod_usu';
You'll need it not only to make your login work, but also to be able to define relations or make find method work for your models , e.g. Usuario::find(123) won't be able to load your user from the database.
Enabling remember-me, that helped in your case, worked by accident. By setting second parameter of attempt() to true you've enabled remember-me feature which results in creating a random identifier that is saved in remember_toke column in your table and later used to load your user data. It's not a proper way of handling your issue, it also will break your application if you decide to disable remember-me in the future.

Related

REST principle of PUT method in Laravel

I am preparing a common and current CRUD-type REST API with the users model that laravel brings by default
<?php
namespace App\Models;
use Laravel\Sanctum\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Auth\Passwords\CanResetPassword;
class User extends Authenticatable implements MustVerifyEmail {
use HasApiTokens, HasFactory, Notifiable, CanResetPassword;
/**
* The attributes that are mass assignable.
*
* #var string[]
*/
protected $fillable = [
'name',
'email',
'password'
];
/**
* The attributes that should be hidden for serialization.
*
* #var array
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime'
];
}
and the routes I generate with Route::apiResource
Route::apiResource('users', UserController);
Since I want to find the user by the email or the id I make a link or explicit injection of the route parameter in the RouteServiceProvider inside the method boot with Route::bind
Route::bind('user', function($userId){
$user = User::where(fn($query) => $query->where('id', $userId)->orWhere('email', $userId))->first();
if(request()->method() === 'PUT') return $user;
return $userId;
});
This is where the main topic comes in, this method (Route::bind ()) should return the instance of a class, if not, it will return an ExceptionModelNotFound 404 - not found, but I would like to be able to receive the null in my controller and thus validate depending on the method (PUT or PATCH) whether to create the new record (which according to REST principles should be able to do a resource if there is no match) or just update an existing one.
My update method of UserController is as follows
public function update(Request $request, User $user){
$input = $request->all();
$method = $request->method();
if($method === 'PUT') $request->validate($this->rules);
$update = [
'PUT' => fn() => ($user) ? $user->update($input) : $user = $user::create($input),
'PATCH' => function() use($user, $input){
foreach ($input as $key => $val){
$user->$key = (!empty($val)) ? $val : $user->$key;
}
$user->save();
return $user;
}
];
return response()->json($update[$method]());
}
Previously I had it without model injection and the method ùpdate worked, something like this
public function update(Request $request, $user){
But due to the explicit injection this no longer takes the parameter, besides that I am only interested in this behavior when updating since this in the rest of the methods helps in the reduction of code and it is fine to handle the 404 - Not found.
I don't know if I'm failing at something or maybe laravel provides a better way to do it which I don't know.
Thanks in advance.
PS: The code is a bit abstracted, it looks a bit messy because I tried to simplify it.
You are not making it easier on yourself by creating a single controller action for both updating and creating users. I have never done it this way, and always define the REST routes like so:
PUT /user/1 = UPDATE
POST /user = CREATE
You should know on the frontend whether you are making a new user or updating an existing one (simply check for an id element for instance).
Also, your current route bind is not the best logic, since that $user query is always executed, even when it is not a PUT request. Nevertheless that code can be simplified much further if you just firstOrFail() that user since you should be separating the functionality into 2 routes anyway.
Personally I never use PATCH, since my PUT routes allow partial updates as well (e.g. only sending an email property update).

How should I correctly construct my unit test in Laravel

I am quite new to unit testing and could do with a bit of guidance. I am trying to write a simple unit test using the Factory pattern in Laravel and phpunit for an application that allows you to add Companies to a database.
I have a Company Model Company.php, a Factory class which uses it CompanyFactory.php, and finally the unit test itself CompaniesTest.php.
Models/Company.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Company extends Model
{
use HasFactory;
protected $table = 'companies';
protected $fillable = [
'name',
'email'
];
}
Database/Factories/CompanyFactory.php
namespace Database\Factories;
use App\Models\Company;
use Illuminate\Database\Eloquent\Factories\Factory;
class CompanyFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = Company::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'name' => $this->faker->name,
'email' => $this->faker->email,
'created_at' => now(),
'updated_at' => now(),
];
}
}
Tests/Feature/CompaniesTest.php
namespace Tests\Unit;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use App\Models\Company;
use Tests\TestCase;
use Illuminate\Support\Str;
use Illuminate\Foundation\Testing\WithFaker;
class CompanyTest extends TestCase
{
use WithFaker, DatabaseTransactions;
public function createCompany($name = NULL)
{
if ($name == NULL) $name = Str::random(6);
$company = Company::factory()->create([
'name' => 'TestName_'.$name
]);
return $company->id;
}
/** #test */
public function company_can_be_created()
{
$name = Str::random(6);
//Create a company
$company_id = $this->createCompany($name);
//Check whether the company name exists in the database
$this->assertDatabaseHas('companies', [
'name' => 'TestName_'.$name
]);
}
}
The test seems to work, but it feels like I might have over-complicated it and probably not followed the correct conventions.
What would be a better way to structure it?
The test looks ok, but what are you actually testing? It seems to me that this test is testing the framework's code, which is actually not what you should do.
Don't test a factory, use it to prepare the data needed before each test. And then run your actual code which you want to test, and assert results.
Update: Continue with CompanyVerifier (see comments). Suppose company can be valid and non-valid. Valid companies can be verified. Then a test may look like:
/** #test */
public function test_valid_company_can_be_verified()
{
// here use a CompanyFactory with some pre-defined data to create "valid" company
$validCompany = $this->createValidCompany();
// here goes the actual code of SUT (system under test)
$verifier = new CompanyVerifier();
$result = $verifier->verify($validCompany);
// here check results
$this->assertTrue($result);
}
The good practice for testing is named AAA (arrange-act-assert). Here the creation of a company with some state is an "arrange" stage. Running tested code is "act". And assert is "assert".
Factories are just a helper for "arrange" stage.

Laravel 6: User models uses UUID instead of auto-increment. Where to update DB session logic?

OK so my User models uses webpatser/laravel-uuid. All migrations are using UUID.
So now my model looks like:
<?php
namespace App\Models;
use App\Models\Traits\Uuid;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
class User extends Authenticatable
{
use Notifiable;
use Uuid;
public $incrementing = false;
public $timestamps = true;
protected $guarded = [
'uuid',
];
protected $keyType = 'string';
protected $primaryKey = 'uuid';
protected $table = 'users';
protected $dates = [
'created_at',
'updated_at',
];
protected $hidden = [
'password',
'remember_token',
];
public function setPasswordAttribute($password): void
{
$this->attributes['password'] = Hash::make($password);
}
}
I want to use database session driver. I created session table via php artisan session:table. All migrations are done. I obviously had to rename existing user_id column. I've changed it to user_uuid. I know it's not enough as I can't find the logic responsible for populating this db table. I guess it's somewhere in the vendor (Illuminate).
Where is the logic to populate my non-default session column?
Now each open the page gives:
So I know what's the issue, what's causing it, how to change it, but I don't know where to start. Thanks for any hints.
I think you would benefit of a custom session handler because the name of the column user_id is hardcoded into the addUserInformation() method.
Extend the existing DatabaseSessionHandler.php and replace the addUserInformation() method so it looks for the correct column name:
class DatabaseUuidSessionHandler extends DatabaseSessionHandler
{
protected function addUserInformation(&$payload)
{
if ($this->container->bound(Guard::class)) {
$payload['user_uuid'] = $this->userId();
}
return $this;
}
}
Register it in one of your service providers:
class SessionServiceProvider extends ServiceProvider
{
public function boot()
{
Session::extend('databaseUuid', function ($app) {
return new DatabaseUuidSessionHandler;
});
}
}
Finally update SESSION_DRIVER in your .env to use the newly created databaseUuid driver.
Remember that this is untested code and should only be used as a guideline of how this could work.

How to implement owen-it/laravel-auditing in a model

I want to create an audit trail in my model. I already installed owen-it/laravel-auditing package via Composer. My question is that how can I implement it in my Model or controller. Please see my code for controller and Model below. Thanks
My Controller :
<?php
namespace App\Http\Controllers;
use App\Events\Test;
use App\Letter;
use App\Notifications\LetterNotification;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Validator;
class LetterController extends Controller
{
public function viewLetter()
{
return view('welcome');
}
/**
* Saves email into database
*
* #param array $data
* #return Letter
*/
protected function create(array $data)
{
$letter = Letter::create([
'email' => $data['email']
]);
$this->letterNotify($letter);
return $letter;
}
/**
* Validates email
*/
public function createLetter(Request $request)
{
$this->validate($request,[
'email' => 'required|email|max:255|unique:letters'
],[
'email.required' => 'Email is required.',
'email.unique' => 'Already registered.',
'email.email' => 'Please put a valid Email address'
]);
$this->create($request->all());
return redirect('/')->with('info','You are now registered.');
}
protected function letterNotify($letter)
{`enter code here`
Notification::send($letter, new LetterNotification($letter));
}
}
For my Model:
<?php
namespace App;
use OwenIt\Auditing\Auditable;
use OwenIt\Auditing\Contracts\Auditable as AuditableContract;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
class Letter extends Model implements AuditableContract
{
use Notifiable;
use Auditable;
protected $fillable = ['email'];
protected $table = 'letters';
}
Like I stated in my comment, the Laravel Auditing package only triggers an audit on a database operation involving an Eloquent model and event (by default, created, updated, deleted, restored).
Having said that, here's a list of steps to create an audit when logging in/out:
Create a listener for the Illuminate\Auth\Events\Login event;
Once fired, update a column in the users table that keeps track of the latest login date/time (latest_login_at, for example);
(Optional) update a column with the previous login date/time (last_login_at, for example);
By doing those updates to the users table, the Auditor kicks in;
You can also listen for the OwenIt\Auditing\Events\Auditing or OwenIt\Auditing\Events\Audited events and apply more logic if needed;
Follow the same steps for the Illuminate\Auth\Events\Logout event;

Laravel 4.2 Array to String conversion error

I am trying to assign a value to $table property in my Model, based on the URL string. There I am getting "Array to string conversion" error. Below are code level details. Can someone please help? Thanks.
I have coded my model __constructor() as shown below.
<?php
use Illuminate\Auth\UserTrait;
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableTrait;
use Illuminate\Auth\Reminders\RemindableInterface;
class Disaster extends Eloquent {
use UserTrait,
RemindableTrait;
/**
* The database table used by the model.
*
* #var string
*/
protected $table;
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = array('password', 'remember_token');
public function __construct($disasterArea, $disaster = "flood") {
$this->table = $disasterArea . '_' . $disaster;
}
}
I am trying to pass required values while Model instantiation from my controller as shown below.
class DisasterController extends \BaseController {
public function getFloodTweets($floodArea){
$flood = new Disaster($floodArea, "floods");
$floodTweets = $flood->orderBy('created_at','desc')->groupBy('tweets')->paginate(10);
$floodAreaUc = ucfirst($floodArea);
return View::make("disaster.floodTweets",['title'=>"$floodAreaUc Flood",'floodTweets'=>$floodTweets,'floodArea'=>$floodAreaUc]);
}
}
}
that means if I trigger an URL like www.example.com/floods/city my model should build the table as 'city_floods' which is the naming convention we are following.
And also, I observed table name is being build correctly but throwing this error. And strange thing is my code works fine when I hard code this table name. i.e.
$this->table = "city_floods" works fine but
$this->table = $disasterArea . '_' . $disaster do not. I don't understand what is the difference. Can someone please suggest where I am doing wrong.
I working on UBUNTU 14.04 with Laravel 4.2 framework.
Edit
Well You cannot pass the data in that way to the eloquent model because the original abstract eloquent model have the first parameter as array! and it will try to put it in this way.
EDIT
As you can see on your screen shot - the model is resolving somewhere with a newInstance method what means that it trying to put (propably) an empty array into you string varible.
The solution
The best option is to split the logic into few Disaster Models that extends the main Disaster model and resolving them from factory like that:
class UsaFloodDisaster extends Disaster
{
protected $table = 'usa_flood';
}
class FranceFloodDisaster extends Disaster
{
protected $table = 'france_flood';
}
(...)
class DisasterFactory
{
public function make($area, $disaster = 'flood')
{
$className = ucfirst(camel_case($area.'_'.$disaster));
return app($className);
}
}
then your controller would looks like:
class DisasterController extends \BaseController {
public function getFloodTweets($floodArea){
$flood = app(DisasterFactory::class)->make($floodArea, "floods");
$floodTweets = $flood->orderBy('created_at','desc')->groupBy('tweets')->paginate(10);
$floodAreaUc = ucfirst($floodArea);
return View::make("disaster.floodTweets",['title'=>"$floodAreaUc Flood",'floodTweets'=>$floodTweets,'floodArea'=>$floodAreaUc]);
}
}

Categories