In a nutshell, I want to create a function that my query scopes can use across multiple models:
public function scopeNormaliseCurrency($query,$targetCurrency) {
return $query->normaliseCurrencyFields(
['cost_per_day','cost_per_week'],
$targetCurrency
);
}
I have got my logic working within this scope function no problem, but I want to make this code available to all my models, as there are multiple currency fields in different tables and I don't want to be replicating the code in each query scope - only specify the columns that need attention.
So, where would I make my function normaliseCurrencyFields? I have extended the Model class as well as used the newCollection keyword to extend Collection but both result in Call to undefined method Illuminate\Database\Query\Builder::normaliseCurrencyFields() errors.
I have looked into Global Scoping but this seems to be localised to a Model.
Am I along the right lines? Should I be targeting Eloquent specifically?
Create an abstract base model that extends eloquent then extend it with the classes you want to have access to it. I do this for searching functions, uuid creation, and class code functions. So that all of my saved models are required to have to certain attributes and access to my searching functions. For instance I created a static search function getobjectbyid(). So that when extended I can call it like so:
$user = User::getobjectbyid('habwiifnbrklsnbbd1938');
Thus way I know I am getting a user object back.
My base model:
<?php
/**
* Created by PhpStorm.
* User: amac
* Date: 6/5/17
* Time: 12:45 AM
*/
namespace App;
use Illuminate\Database\Eloquent\Model as Eloquent;
abstract class Model extends Eloquent
{
protected $guarded = [
'class_code',
'id'
];
public $primaryKey = 'id';
public $incrementing = false;
public function __construct($attributes = array()) {
parent::__construct($attributes); // Eloquent
$this->class_code = \App\Enums\EnumClassCode::getValueByKey(get_class($this));
$this->id = $this->class_code . uniqid();
return $this;
}
public static function getObjectById($id){
$class = get_called_class();
$results = $class::find($id);
return $results;
}
public static function getAllObjects(){
$class = get_called_class();
return $class::all();
}
my user model:
<?php
namespace App;
use Mockery\Exception;
use Illuminate\Support\Facades\Hash;
use Illuminate\Auth\Authenticatable;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use App\Model as Model;
class User extends Model implements AuthenticatableContract, CanResetPasswordContract
{
use Authenticatable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'contact', 'username', 'email_address'
];
/**
* The column name of the "remember me" token.
*
* #var string
*/
protected $rememberTokenName = 'remember_token';
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'remember_token', 'active'
];
/**
* the attributes that should be guarded from Mass Assignment
*
* #var array
*/
protected $guarded = [
'created_at', 'updated_at', 'password_hash'
];
/**
* Define table to be used with this model. It defaults and assumes table names will have an s added to the end.
*for instance App\User table by default would be users
*/
protected $table = "user";
/**
* We have a non incrementing primary key
*
* #var bool
*/
public $incrementing = false;
/**
* relationships
*/
public function contact(){
// return $this->hasOne(Contact::class, 'id', 'contact_id');
return $this->hasOne(Contact::class);
}
public function customers(){
// return $this->hasOne(Contact::class, 'id', 'contact_id');
return $this->hasMany(Customer::class);
}
/**
* User constructor.
* #param array $attributes
*/
public function __construct($attributes = array()) {
parent::__construct($attributes); // Eloquent
// Your construct code.
$this->active = 1;
return $this;
}
/**
* #param $password string
* set user password_hash
* #return $this
*/
public function setPassword($password){
// TODO Password Validation
try{
$this->isActive();
$this->password_hash = Hash::make($password);
$this->save();
} catch(\Exception $e) {
dump($e->getMessage());
}
return $this;
}
/**
* Returns whether or not this use is active.
*
* #return bool
*/
public function isActive(){
if($this->active) {
return true;
} else {
Throw new Exception('This user is not active. Therefore you cannot change the password', 409);
}
}
public function getEmailUsername(){
$contact = Contact::getObjectById($this->contact_id);
$email = Email::getObjectById($contact->email_id);
return $email->username_prefix;
}
/**
* #return string
*
* getFullName
* returns concatenated first and last name of user.
*/
public function getFullName(){
return $this->first_name . ' ' . $this->last_name;
}
/**
* Get the name of the unique identifier for the user.
*
* #return string
*/
public function getAuthIdentifierName(){
return $this->getKeyName();
}
/**
* Get the unique identifier for the user.
*
* #return mixed
*/
public function getAuthIdentifier(){
return $this->{$this->getAuthIdentifierName()};
}
/**
* Get the password for the user.
*
* #return string
*/
public function getAuthPassword(){
return $this->password_hash;
}
/**
* Get the token value for the "remember me" session.
*
* #return string
*/
public function getRememberToken(){
if (! empty($this->getRememberTokenName())) {
return $this->{$this->getRememberTokenName()};
}
}
/**
* Set the token value for the "remember me" session.
*
* #param string $value
* #return void
*/
public function setRememberToken($value){
if (! empty($this->getRememberTokenName())) {
$this->{$this->getRememberTokenName()} = $value;
}
}
/**
* Get the column name for the "remember me" token.
*
* #return string
*/
public function getRememberTokenName(){
return $this->rememberTokenName;
}
/**
* Get the e-mail address where password reset links are sent.
*
* #return string
*/
public function getEmailForPasswordReset(){
}
/**
* Send the password reset notification.
*
* #param string $token
* #return void
*/
public function sendPasswordResetNotification($token){
}
public function validateAddress(){
}
}
a TestController:
public function test(){
$user = User::getObjectById('USR594079ca59746');
$customers = array();
foreach ($user->customers as $customer){
$contact = Contact::getObjectById($customer->contact_id);
$name = PersonName::getObjectById($contact->personname_id);
$c = new \stdClass();
$c->id = $customer->id;
$c->name = $name->preferred_name;
$customers[] = $c;
}
$response = response()->json($customers);
return $response;
}
Take note on how getObjectById is extended and available to my other classes that extend my base model. Also I do not have to specify in my user model an 'id' or 'class_code' and when my user model is constructed it calls the parent constructor which is the constructor on my base model that handles 'id' and 'class_code'.
Related
I'm looking for a solution to optimize my Code using Laravel Eloquent.
My issue is that I want to add Attributes conditionally, and this Attributes is basically the a transformed many-to-many relationship.
At the moment I have this in my controller (simplified):
<?php
namespace App\Http\Controller;
/**
* Class Category
*/
class Category extends Controller
{
/**
* #return Collection
*/
public function index()
{
return Category::withCount('countries')->get();
}
/**
* #param int $id
*
* #return Category
*/
public function show($id)
{
$result = Category::where('id', $id)
->with('countries')
->firstOrFail();
$result->countries_list = '';
return $result;
}
}
My Category model looks like this (simplified):
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
/**
* Class Category
*/
class Category extends Model
{
/**
* The accessors to append to the model's array form.
*
* #var array
*/
protected $appends = [
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'countries',
];
/**
* #return string
*/
public function getCountriesCountAttribute()
{
return trans_choice('labels.countries', $this->original['countries_count']);
}
/**
* #return
*/
public function getCountriesListAttribute()
{
return $this->countries->pluck('alpha_2');
}
/**
* Get the related Countries.
*/
public function countries()
{
return $this->belongsToMany(
Country::class,
'category_country',
'category_id',
'country_id'
);
}
}
The Country Model is just a list of Countries with id, name, the Alpha2 Code, etc. I can't use the protected $appends to add countries_list because than the the list would be always included.
I also can't change my Countries model because this is used in several other occurrences.
What I'm looking for is a way to optimize the code in the controller to this:
<?php
namespace App\Http\Controller;
/**
* #return Collection
*/
public function index()
{
return Category::withCount('countries')->get();
}
/**
* #param int $id
*
* #return Category
*/
public function show($id)
{
return Category::where('id', $id)
->withAttribute('countries_list') // An array of all country aplha 2 codes
->firstOrFail();
}
You can access the countries_list attribute after querying (don't include it in your query).
public function show($id)
{
$category = Category::findOrFail($id);
$list = $category->countries_list; // this calls getCountriesListAttribute()
}
I have the following code:
$users = User::with('profile')->paginate($count);
and:
$roles = Role::withCount('users')->get();
Now using laravel-debugbar and when I open my users page what I see is that 107 queries are being executed making the page load really slowly and I have a feeling that when I add more users the number total number of queries executed is going to increase. I have looked at the code and right now I can't tell what is wrong with it or how to reduce the number of queries being generated.
These are my models:
User.php
<?php
namespace App\Models;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Kodeine\Acl\Traits\HasRole;
class User extends Authenticatable
{
use HasRole, Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
protected $table = "users";
protected $appends = ['role'];
public function profile()
{
return $this->hasOne('App\Models\Profile');
}
public function getRoleAttribute() {
return $this->getRoles();
}
}
Role.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Kodeine\Acl\Traits\HasPermission;
use Cviebrock\EloquentSluggable\Sluggable;
class Role extends Model
{
use HasPermission, Sluggable;
protected $table = "roles";
/**
* The attributes that are fillable via mass assignment.
*
* #var array
*/
protected $fillable = ['name', 'slug', 'description'];
/**
* Roles can belong to many users.
*
* #return Model
*/
public function users()
{
return $this->belongsToMany('App\Models\User', 'role_user', 'role_id', 'user_id');
}
/**
* List all permissions
*
* #return mixed
*/
public function getPermissions()
{
return $this->getPermissionsInherited();
}
/**
* Checks if the role has the given permission.
*
* #param string $permission
* #param string $operator
* #param array $mergePermissions
* #return bool
*/
public function can($permission, $operator = null, $mergePermissions = [])
{
$operator = is_null($operator) ? $this->parseOperator($permission) : $operator;
$permission = $this->hasDelimiterToArray($permission);
$permissions = $this->getPermissions() + $mergePermissions;
// make permissions to dot notation.
// create.user, delete.admin etc.
$permissions = $this->toDotPermissions($permissions);
// validate permissions array
if ( is_array($permission) ) {
if ( ! in_array($operator, ['and', 'or']) ) {
$e = 'Invalid operator, available operators are "and", "or".';
throw new \InvalidArgumentException($e);
}
$call = 'canWith' . ucwords($operator);
return $this->$call($permission, $permissions);
}
// validate single permission
return isset($permissions[$permission]) && $permissions[$permission] == true;
}
/**
* #param $permission
* #param $permissions
* #return bool
*/
protected function canWithAnd($permission, $permissions)
{
foreach ($permission as $check) {
if ( ! in_array($check, $permissions) || ! isset($permissions[$check]) || $permissions[$check] != true ) {
return false;
}
}
return true;
}
/**
* #param $permission
* #param $permissions
* #return bool
*/
protected function canWithOr($permission, $permissions)
{
foreach ($permission as $check) {
if ( in_array($check, $permissions) && isset($permissions[$check]) && $permissions[$check] == true ) {
return true;
}
}
return false;
}
/**
* Return the sluggable configuration array for this model.
*
* #return array
*/
public function sluggable()
{
return [
'slug' => [
'source' => 'name'
]
];
}
}
Profile.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Profile extends Model
{
protected $table = "profiles";
public function user()
{
return $this->belongsTo('App\Models\User');
}
}
This seems to be the query being repeated:
select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id`, `role_user`.`created_at` as `pivot_created_at`, `role_user`.`updated_at` as `pivot_updated_at` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = '4'
Edit:
The issue seems to be coming from my roles, I'm looking at the code in my controller and in my model and I can't tell what is wrong. I changed $roles = Role::withCount('users')->get(); to $roles = Role::with('users')->withCount('users')->get(); and now I have fewer queries generated (71 still a lot).
It's the appends role attribute on the user model that is causing so many excessive queries. As a test comment out appends roles and test how many queries run then. You'll likely need to lazy load roles to keep the number of queries minimized.
i have address Book table and user table i am assigning
the many user in my address book while i am created everything is fine(ok)
but when i am editing every data back in my form without assign user .
how can i get the user in editing form ?? this is my Address Book Controller
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests\AddressRequest;
use App\Http\Requests;
use App\Models\Address;
use App\Models\User;
use App\Http\Controllers\Controller;`enter code here`
use Illuminate\Pagination\Paginator;
use Auth;
use DB;
use Session;
class AddressesController extends Controller
{
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index(Request $request)
{
Session::forget('searchaddress');
$addresses = Address::orderby('company_name');
$company_name = $request->input('company_name');
if(!empty($company_name)) {
//$addresses->where('company_name','LIKE','%'.$company_name.'%');
$addresses->where('company_name','LIKE','%'.$company_name.'%');
Session::set('searchaddress', $company_name);
}
$addresses = $addresses->paginate(5);
return view('address.index',compact('addresses'));
}
/**
* Show the form for creating a new resource.
*
* #return Response
*/
public function create()
{
$users = User::lists('first_name','id');
return view('address.create',compact('users'));
}
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store(AddressRequest $request)
{
$address = Address::create($request->all());
$firstname = Auth::user()->first_name;
$lastname = Auth::user()->last_name;
$address->created_by =$firstname." ".$lastname;
$address->users()->attach($request->input('user_list'));
$address->save();
return redirect('/addresses');
}
/**
* Display the specified resource.
*
* #param int $id
* #return Response
*/
public function show($id)
{
$address = Address::find($id);
return view('address.show',compact('address'));
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return Response
*/
public function edit($id)
{ $users = User::lists('first_name','id');
$address = Address::findorFail($id);
return view('address.edit',compact('address','users'));
}
/**
* Update the specified resource in storage.
*
* #param int $id
* #return Response
*/
public function update( AddressRequest $request ,$id)
{
$address = Address::findOrFail($id);
$address->update($request->all());
$address->users()->sync($request->input('user_list'));
return redirect('/addresses');
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return Response
*/
public function destroy($id)
{
$address = Address::find($id);
$address->delete();
return redirect('/addresses');
}
}
and that is my AddressBook Model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Address extends Model
{
protected $fillable = [
'created_by',
'company_name',
'phone',
'email', 'address','comment'
];
public function users()
{
return $this->belongsToMany('App\Models\User')->withTimestamps();
}
public function getUserListAttribute()
{
return $this->users->lists('id');
}
}
[![enter image description here][1]][1]
[1]: http://i.stack.imgur.com/D4jXQ.png
You have belongsToMany relation, so in your edit action you also should get current Address users like this
public function edit($id)
{ $users = User::lists('first_name','id');
$address = Address::findorFail($id);
$address_users = $address->users->lists('id')->toArray();
return view('address.edit',compact('address','users', 'address_users'));
}
then in your view in select you should intersect arrays of $users and $address_users to get selected options.
{!! Form::select('user_list[], $users, isset($address_users) ? $address_users : null, ['id' => 'users_list', 'class' => 'form-control', 'multiple']) !!}
to avoid
isset($address_users) ? $address_users : null
you can define empty address_users array in your create method and do it like this
$address_users
Here is the relationship 1 code:
/**
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function address()
{
return $this->hasMany('App\IPAddress', 'group_id');
}
and relationship 2 code:
/**
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function group()
{
return $this->belongsTo('App\IPGroups');
}
I want to get all ip addresses that belongs to specified group. I don't want to write raw queries, I need to be done with querying relationship. Does anyone has an idea?
I tried to do something like this:
/**
* Get IP Addresses of specified group
* #param Request $request
* #return mixed
*/
public function getIP(Request $request)
{
$group = IPGroups::findOrFail($request->group_id);
return $group->address;
}
but I need to add one where statement where I can pick only active ip addresses.
Here is the model 1 code:
namespace App;
use Illuminate\Database\Eloquent\Model;
class IPGroups extends Model
{
/**
* Working Table
* #var string
*/
protected $table = 'ip_groups';
/**
* Guarded Values From Mass Assignment
* #var array
*/
protected $guarded = [ 'id' ];
/**
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function address()
{
return $this->hasMany('App\IPAddress', 'group_id');
}
}
and the second model code:
namespace App;
use Illuminate\Database\Eloquent\Model;
class IPAddress extends Model
{
/**
* Working Table
* #var string
*/
protected $table = 'ips';
/**
* Protected Values From Mass Assignment
* #var array
*/
protected $fillable = [ 'group_id', 'ip', 'description', 'status' ];
/**
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function group()
{
return $this->belongsTo('App\IPGroups');
}
}
Try this, getting only the addresses with status as 'Active':
return $group->address->where('status','Active');
The reason this doesn't work:
return $group->address->where('status','=','Active');
is that the where we are using here is the where of the class Collection, which doesn't accept a comparator as second parameter as the where of the Models do.
I'm having trouble with logging users in, everything appears to be in the right place, I get no errors in the log, but users fail to log in, I am using the correct credentials that are in my database.
Please note I have a different set up to the normal one:
My table is called test_users
My model sits in a separate namespace called Test
Here's my code:
In config>auth I have set:
'model' => '\Test\User',
'table' => 'test_users',
Here is how I call the Auth:
public function logIn()
{
$input = Input::all();
$credentials = array('email' => $input['email'], 'password' => $input['password']);
$input['remember-me'] = isset($input['remember-me']) ? true : false;
if(Auth::attempt($credentials, $input['remember-me']))
{
$this->output['message'] = 'ok';
}
else
{
$this->output['message'] = 'fail';
}
return $this->output;
}
Here's my model:
<?php namespace Test;
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
use Eloquent;
class User extends Eloquent implements UserInterface, RemindableInterface {
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'test_users';
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = array('password');
/**
* 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;
}
}
Auth::attempt checks for a hashed password. It appears you might be trying to set them with plaintext. Try setting your passwords with Hash::make('password') if you aren't already.