Handling relationships in Laravel - php

I am building a system to support referral programs. It allows you to track links, programs and for how long will cookie last. Also allows you to easily decide how will user benefit after referring other user to for example sign up.
This' for generating unique referral code on creating a referral link. I am able to use this code to track the link and and relate it to original user who shared it. Factory method getReferral is creating single referral link for each user for a given program.
// ReferralLink Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Ramsey\Uuid\Uuid;
class ReferralLink extends Model
{
protected $fillable = ['user_id', 'referral_program_id'];
protected static function boot()
{
static::creating(function (ReferralLink $model) {
$model->generateCode();
});
parent::boot();
}
private function generateCode()
{
$this->code = (string)Uuid::uuid1();
}
public static function getReferral($user, $program)
{
return static::firstOrCreate([
'user_id' => $user->id,
'referral_program_id' => $program->id
]);
}
public function getLinkAttribute()
{
return url($this->program->uri) . '?ref=' . $this->code;
}
public function user()
{
return $this->belongsTo(User::class);
}
public function program()
{
return $this->belongsTo(ReferralProgram::class, 'referral_program_id');
}
public function relationships()
{
return $this->hasMany(ReferralRelationship::class);
}
}
StoreReferralCode middleware
use App\ReferralLink;
// ...
public function handle($request, Closure $next)
{
$response = $next($request);
if ($request->has('ref')){
$referral = ReferralLink::whereCode($request->get('ref'))->first();
$response->cookie('ref', $referral->id, $referral->lifetime_minutes);
}
return $response;
}
UserReferred Event handler
class UserReferred
{
use SerializesModels;
public $referralId;
public $user;
public function __construct($referralId, $user)
{
$this->referralId = $referralId;
$this->user = $user;
}
public function broadcastOn()
{
return [];
}
}
And of course this' how I am broadcasting the event on RegistrationController
protected function create(array $data)
{
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
event(new \App\Events\UserReferred(request()->cookie('ref'), $user));
return $user;
}
I am not able to create a relationship in the DB on grounds of who shared a link with who. What could be wrong with my code ?
//referral_relationships schema
Schema::create('referral_relationships', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('referral_link_id');
$table->integer('user_id');
$table->timestamps();
});

Related

How to make each authenticated user only see their own product

I'm trying to figure out how an authenticated user can only see products from their dashboard, i.e., every user has unique products list, and be able to create his own products. What i see now is if any user creates,delete or list product all users get affected.
I have tried to search other tutorials but didn't get the solution.
web.php
Route::group(['prefix'=>'seller', 'middleware'=> ['auth']],function() {
Route::get('/',function (){
return view('seller.index', compact('products'));
});
Route::resource('product', 'productController');
Route::get('/seller', 'ProductController#seller')->name('seller');
});
User.php
public function products()
{
return $this->hasMany(Products_model::class);
}
Products_model
class products_model extends Model
{
protected $table='products';
protected $primaryKey='id';
protected $fillable= ['pro_name','pro_price','pro_info','image','stock','category_id'];
}
ProductController
class productController extends Controller
{
public function index()
{
$products=products_model::all();
return view('seller.product.index',compact('products'));
}
public function user()
{
return $this->belongsTo(User::class);
}
public function create()
{
return view('seller.product.create');
}
public function seller()
{
$products=products_model::all();
return view('seller.product.index',compact('products'));
}
public function store(Request $request)
{
$formInput=$request->except('image');
$this->validate($request, [
'pro_name'=> 'required',
'pro_price'=> 'required',
'pro_info'=> 'required',
'image'=>'image|mimes:png,jpg,jpeg|max:10000'
]);
$image=$request->image;
if($image){
$imageName=$image->getClientOriginalName();
$image->move('images', $imageName);
$formInput['image']=$imageName;
}
products_model::create($formInput);
return redirect()->back();
}
public function show($id)
{
//
}
public function edit($id)
{
//
}
public function update(Request $request, $id)
{
//
}
public function destroy($id)
{
$deleteData=products_model::findOrFail($id);
$deleteData->delete();
return redirect()->back();
}
}
I want every user to have their unique dashboard, which means if a user deletes or creates a product it should only show in his dashboard without affecting others.
Wherever you need to display only the authenticated user's products, change your query to filter out other peoples products:
public function controllerAction(Request $request)
{
$userId = $request->user()->id;
// or $userId = Auth::id(); (Via the Auth facade)
// or $userId = auth()->id();
$products = products_model::where('user_id', $userId)->get();
}
In your product model you need add the user_id to able relate the tables user with products:
class products_model extends Model
{
protected $table='products';
protected $primaryKey='id';
protected $fillable= ['user_id', 'pro_name','pro_price','pro_info','image','stock','category_id'];
}
After in you controller you can filter the products by user and return these, at create new product can get the user logged id and put in new product

Lumen JWT Authentication without Eloquent and MySQL

I'm developing an API that uses not standard database connection that is not supported by Laravel by default. Because of that I am not able to use Eloquent Models to create JWT tokens and authenticate users. I have already implemented a custom user model:
use App\Repositories\Technician as TechnicianRepository;
use Illuminate\Contracts\Auth\Authenticatable;
use Tymon\JWTAuth\Contracts\JWTSubject;
class Technician implements Authenticatable, JWTSubject {
public function __construct(array $data) {
$this->repository = new TechnicianRepository;
foreach ($this->repository->create($data) as $attribute => $value) {
$this->{$attribute} = $value;
}
}
public function getAuthIdentifierName() {
return 'id';
}
public function getAuthIdentifier() {
return $this->{$this->getAuthIdentifierName()};
}
public function getAuthPassword() {
return decrypt($this->password);
}
public function getRememberToken() {}
public function setRememberToken($value) {}
public function getRememberTokenName() {}
public function getJWTIdentifier()
{
return $this->id;
}
public function getJWTCustomClaims()
{
return [];
}
}
Using this model I am able to successfully generate a JWT token in my AuthController like this:
$technician = new Technician([
'email' => $request->email,
'phone' => $request->phone,
'password' => encrypt($request->password)
]);
$token = app('auth')->login($technician);
However I have no idea how to furtherly authenticate users based on the generated JWT token that is passed with a request.
For now I have the following contents in boot method of AuthServiceProvider:
public function boot()
{
$this->app['auth']->viaRequest('api', function ($request) {
return app('auth')->setRequest($request)->user();
});
}
And the following logic in Authenticate middleware:
if ($this->auth->guard($guard)->guest()) {
return Response::fail([
'auth' => array('Access denied - authorization is required')
], 401);
}
return $next($request);
Even if providing the right JWT token for a user, the middleware denies access.
Any help is appreciated, because I have no idea how to develop user authentication furtherly.

Laravel call to a member function addEagerConstraints() on boolean

I'm getting the following error whenever i go on to a users page, its supposed to show if the authenticated user is already following the user that the profile is on.
Could this be a problem with the relationship setup, it hasMany
Stack trace
local.ERROR: Call to a member function addEagerConstraints() on
boolean {"userId":1,"email":"fakeemail#aol.com","exception":"[object]
(Symfony\Component\Debug\Exception\FatalThrowableError(code: 0):
Call to a member function addEagerConstraints() on boolean at
/Applications/MAMP/htdocs/elipost/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php:522)"}
[]
UserController.php
public function getProfile($user)
{
$users = User::with([
'posts.likes' => function($query) {
$query->whereNull('deleted_at');
$query->where('user_id', auth()->user()->id);
},
'follow',
'follow.follower'
])->with(['followers' => function($query) {
$query->with('follow.followedByMe');
$query->where('user_id', auth()->user()->id);
}])->where('name','=', $user)->get();
$user = $users->map(function(User $myuser){
return ['followedByMe' => $myuser->followers->count() == 0];
});
if (!$user) {
return redirect('404');
}
return view ('profile')->with('user', $user);
}
MyFollow(model)
<?php
class MyFollow extends Model
{
use SoftDeletes, CanFollow, CanBeFollowed;
protected $fillable = [
'user_id',
'followable_id'
];
public $timestamps = false;
protected $table = 'followables';
public function follower()
{
return $this->belongsTo('App\User', 'followable_id');
}
public function followedByMe()
{
return $this->follower->getKey() === auth()->id();
}
}
MyFollow
use Overtrue\LaravelFollow\Traits\CanFollow;
use Overtrue\LaravelFollow\Traits\CanBeFollowed;
class MyFollow extends Model
{
use SoftDeletes, CanFollow, CanBeFollowed;
protected $fillable = [
'user_id',
'followable_id'
];
public $timestamps = false;
protected $table = 'followables';
public function follower()
{
return $this->belongsTo('App\User', 'followable_id');
}
public function followedByMe()
{
return $this->follower->getKey() === auth()->id();
}
}
Post
class Post extends Authenticatable
{
protected $fillable = [
'title',
'body',
'user_id',
'created_at',
];
public function user()
{
return $this->belongsTo(User::class);
}
public function comments()
{
return $this->hasMany('App\Comment');
}
public function likes()
{
return $this->hasMany('App\Like');
}
public function likedByMe()
{
foreach($this->likes as $like) {
if ($like->user_id == auth()->id()){
return true;
}
}
return false;
}
}
Likes
<?php
namespace App;
use App\Post;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Like extends Model
{
use SoftDeletes;
protected $fillable = [
'user_id',
'post_id'
];
}
User(model)
class User extends Authenticatable
{
use Notifiable,CanFollow, CanBeFollowed;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function posts()
{
return $this->hasMany(Post::class);
}
public function images()
{
return $this->hasMany(GalleryImage::class, 'user_id');
}
public function likes()
{
return $this->hasMany('App\Like');
}
public function follow()
{
return $this->hasMany('App\MyFollow');
}
public function comments()
{
return $this->hasMany('App\Comment');
}
}
As Jonas Staudenmeir stated, followedByMe isn't a relationship, it's a regular function and what it does is returning a boolean. I'm confused at why you've got a follow on your user model and trying to get information from the follow's follower? Just simplify, I see too much unneeded eager loading here.
Searching by indexed elements (id) > searching by name, any day of the week
Edit:
UserController
public function getProfile(Request $request, $id)
{
//$request->user() will get you the authenticated user
$user = User::with(['posts.likes','followers','follows','followers.follows'])
->findOrFail($request->user()->id);
//This returns the authenticated user's information posts, likes, followers, follows and who follows the followers
//If you wish to get someone else's information, you just switch
//the $request->user()->id to the $id if you're working with id's, if you're
//working with names, you need to replace findOrFail($id) with ->where('name',$name')->get() and this will give you
//a collection, not a single user as the findOrFail. You will need to add a ->first() to get the first user it finds in the collection it results of
//If you're planning on getting an attribute (is_following = true) to know if
//the authenticated user is following, you can use an accessor in the User model and write this after you've fetched the instance of the User
//$user->append('is_following');
return view ('profile')->with('user', $user);
}
User Model
//Accessor
//People who this user follows
public function getIsFollowingAttribute()
{
return MyFollow::where('followable_id',$this->attributes['id'])->where('user_id',Auth()->user()->id)->count() > 0 ? true : false;
}
//Relationships
//People who this user follows
public function follow()
{
return $this->hasMany('App\MyFollow','user_id','id');
}
//People who follows this user
public function followers()
{
return $this->hasMany('App\MyFollow','followable_id','id');
}
//Posts of this user
public function posts()
{
return $this->hasMany('App\Post','user_id','id');
}
//Likes of this user, not sure about this one tho, we're not using this for now but it could come in handy for you in the future
public function likes()
{
return $this->hasManyThrough('App\Likes','App\Post','user_id','user_id','id');
}
Post Model
//Who like this post
public function likes()
{
return $this->hasMany('App\Post','user_id','id');
}
MyFollow Model
//Relationships
//People who follow this user
public function followers()
{
return $this->hasMany('App\MyFollow','followable_id','user_id');
}
//Relationships
//People who this user follows
public function follow()
{
return $this->hasMany('App\MyFollow','user_id','followable_id');
}
With the help of #abr i found a simple fix, simple solution.
MyFollow.php(model)
public function followers()
{
return $this->hasMany('App\MyFollow','followable_id','user_id');
}
//Relationships
//People who this user follows
public function follow()
{
return $this->hasMany('App\MyFollow','user_id','followable_id');
}
User.php(model)
public function getIsFollowingAttribute()
{
return MyFollow::where('followable_id',$this->attributes['id'])->where('user_id',Auth()->user()->id)->count() > 0 ? true : false;
}
public function follow()
{
return $this->hasMany('App\MyFollow');
}
UserController.php
public function getProfile($user)
{
$users = User::with(['posts.likes' => function($query) {
$query->whereNull('deleted_at');
$query->where('user_id', auth()->user()->id);
}, 'followers','follow.followers'])
->with(['followers' => function($query) {
}])->where('name','=', $user)->get();
$user = $users->map(function(User $myuser){
$myuser['followedByMe'] = $myuser->getIsFollowingAttribute();
return $myuser;
});
if(!$user){
return redirect('404');
}
return view ('profile')->with('user', $user);
}
it works now. :)

Using a policy's this->authorize() check in a laravel controller inside a store() method

So I was reading about using laravel policies for granting authorities on the resources of my application but there seems to be a problem there though I followed the tutorial.
I have a user model which can't be created via HTTP requests except by other users who have the Entrust role of 'Admin' or 'Broker'. What I understood and succeeded to make it work on other actions like indexing users was the following:
Inside the AuthServiceProvider.php inside the private $policies array, I registered that User class with the UserPolicy class like that
class AuthServiceProvider extends ServiceProvider {
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
User::class => UserPolicy::class,
Insured::class => InsuredPolicy::class
];
public function boot(GateContract $gate)
{
$this->registerPolicies($gate);
}
}
Define the UserPolicy controller class:
class UserPolicy {
use HandlesAuthorization;
protected $user;
public function __construct(User $user) {
$this->user = $user;
}
public function index(User $user) {
$is_authorized = $user->hasRole('Admin');
return $is_authorized;
}
public function show(User $user, User $user_res) {
$is_authorized = ($user->id == $user_res->id);
return $is_authorized;
}
public function store() {
$is_authorized = $user->hasRole('Admin');
return $is_authorized;
}
}
Then inside the UserController class, before performing the critical action I use this->authorize() check to halt or proceed depending on the privilege of the user
class UserController extends Controller
{
public function index()
{
//temporary authentication here
$users = User::all();
$this->authorize('index', User::class);
return $users;
}
public function show($id)
{
$user = User::find($id);
$this->authorize('show', $user);
return $user;
}
public function store(Request $request) {
$user = new User;
$user->name = $request->get('name');
$user->email = $request->get('email');
$user->password = \Hash::make($request->get('password'));
$this->authorize('store', User::class);
$user->save();
return $user;
}
}
The problem is that $this->authorize() always halts the process on the store action returning exception: This action is unauthorized.
I tried multiple variations for arguments of the authorize() and can't get it to work like the index action
In store() function of UserPolicy::class you are not passing the User model object:
public function store(User $user) {
$is_authorized = $user->hasRole('Admin');
return true;
}
missing argument User $user.
Maybe this is the cause of the problem.

Laravel 4 : Authentication when having a many to many relationship

I have a users, roles and role_users table. In the roles table I have a user and admin value. Now I want to be able to edit users when the role of a user is admin. I don't know how to access the role_name == 'admin' in laravel.
When I use this it works :
#if(Auth::user()->user_username == 'Gilko')
But I want to be able to access this role_name == 'admin'
role_users migration
public function up()
{
Schema::create('role_users', function($table)
{
$table->increments('role_user_id');
$table->integer('role_id')->unsigned();
$table->integer('user_id')->unsigned();
});
Schema::table('role_users', function($table)
{
$table->foreign('role_id')
->references('role_id')->on('roles');
//->onDelete('cascade');
$table->foreign('user_id')
->references('user_id')->on('users');
//->onDelete('cascade');
});
}
User model :
class User extends Eloquent implements UserInterface, RemindableInterface {
protected $table = 'users';
protected $primaryKey = 'user_id';
protected $hidden = ["password"];
public function getAuthIdentifier()
{
return $this->getKey();
}
public function getAuthPassword()
{
return $this->user_password;
}
public function getReminderEmail()
{
return $this->email;
}
public function user()
{
return $this->hasMany('Checklist', 'user_id', 'user_id');
}
public function roles(){
return $this->belongsToMany('Role', 'role_users', 'user_id', 'role_id');
}
public function getRememberToken()
{
//return $this->remember_token;
}
public function setRememberToken($value)
{
//$this->remember_token = $value;
}
public function getRememberTokenName()
{
//return 'remember_token';
}
}
You should be able to do something like this:
if (Auth::user()->roles()->where('name', 'admin')->first())
first will return null if there's no result.
You can also use firstOrFail: http://laravel.com/docs/eloquent
try (Auth::user()->roles()->where('name', 'admin')->firstOrFail()) {
// User has admin role
} catch (ModelNotFoundException $e) {
// User doesn't have admin role
}
Edit Just realized I had a brain fart. Corrected the code :-)
You can add a function which checks for this on your user model...
public function isAdmin()
{
return (bool)$this->roles()->where('name', 'admin')->count();
}
And then you can easily use it with...
#if(Auth::user()->isAdmin())

Categories