Laravel 4 : Where to put model helper methods? - laravel

Where is the best place to put helper methods for Laravel Eloquent models?
At the moment I'm creating a BaseModel that extends Eloquent and putting extra functionality in there. Then all my models extend BaseModel. It works.. but it doesn't feel right.
For Example is my BaseModel at the moment...
class BaseModel extends Eloquent {
protected static function getEnumValues($table, $field)
{
$test=DB::select(DB::raw("show columns from {$table} where field = '{$field}'"));
preg_match('/^enum\((.*)\)$/', $test[0]->Type, $matches);
foreach( explode(',', $matches[1]) as $value )
{
$enum[] = trim( $value, "'" );
}
return $enum;
}
protected static function convertDate($date)
{
if(!isset($date)) return;
$new_date = DateTime::createFromFormat('d/m/Y', $date);
$formated_date = $new_date->format('Y-m-d');
return $formated_date;
}
}
And I use them in my models like...
public static function boot()
{
parent::boot();
static::creating(function($campaign)
{
$campaign->sale_date = static::convertDate($campaign->sale_date);
$campaign->sold_date = static::convertDate($campaign->sold_date);
});
}
Where is the best place to put model helper methods like this?
Feedback appreciated, thanks!

You can write a static helper class then call it from your model.
You can refer to my blog post about how to add custom class. Or you can always utilize composer autoload using psr-0 or psr-4

What I did is...I created a Helper Directory in and inside that I created a GlobalHelper.php File.
app/Helper/GlobalHelper.php
In GlobalHelper.php
namespace App\Helper;
use App\Command;
class GlobalHelper{
public static function getCommands(){
$getCommands = Command::all();
return $getCommands;
}
}
Than in controller or anywhere call like this:
use App\Helper\GlobalHelper;
$getCommands = GlobalHelper::getCommands();

Related

Laravel : How to Pass an attribute to Eloquent model constructor

I want to use strtolower() before saving data in database for 5 attributes,
I'm using this code in Model
public function setFirstNameAttribute($value)
{
$this->attributes['firstName'] = strtolower($value);
}
public function setLastNameAttribute($value)
{
$this->attributes['lastName'] = strtolower($value);
}
public function setUserNameAttribute($value)
{
$this->attributes['userName'] = strtolower($value);
}
... etc
Can I use the __construct method instead of the above code?
There are two ways first one, to use boot method directly (preferred for small changes in model like in your question)
Method 1 :
we can directly use the boot method,
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Mymodel extends Model
{
public static function boot()
{
parent::boot();
static::saving(function ($model) {
// Remember that $model here is an instance of MyModel
$model->firstName = strtolower($model->firstName);
$model->lastName = strtolower($model->lastName);
$model->userName = strtolower($model->userName);
// ...... other attributes
});
}
}
Method 2 :
So we can use here a simple trait with a simple method for generating a strtolower() for a string.This is preferred when you have to do bigger changes in your model while performing operations in model like saving, creating etc. Or even if you want to use the same property in multiple models.
Create a trait MyStrtolower
<?php
namespace App\Traits;
trait MyStrtolower
{
public function mystrtolower($string)
{
return strtolower($string);
}
}
We can now attach this trait to any class that we want to have the mystrtolower method.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\Traits\MyStrtolower;
class Mymodel extends Model
{
use MyStrtolower; // Attach the MyStrtolower trait to the model
public static function boot()
{
parent::boot();
static::saving(function ($model) {
// Remember that $model here is an instance of MyModel
$model->firstName = $model->mystrtolower($model->firstName);
$model->lastName = $model->mystrtolower($model->lastName);
$model->userName = $model->mystrtolower($model->userName);
// ...... other attributes
});
}
}
If you want to not repeat all these lines of code for every model you make, make the trait configurable using abstract methods so that you can dynamically pass the attribute names for which you want to lower case string, like employee_name is Employee Model and user_name in User Model.

Laravel: Creating a common function for all models

In my laravel(7.x) application, I have a common functionality to show the count of all the active and inactive records in all the modules. Therefore, I am obligated to repeat the same functionality on every module.
For example: Device, DeviceType, DeviceCompany, etc models have a same method called _getTotal and everywhere that _getTotal method is doing the same work.
Device.php
class Device extends Model
{
protected $table = 'devices';
...
public function _getTotal($status = \Common::STATUS_ACTIVE)
{
return self::where([
'status' => $status
])->count() ?? 0;
}
}
DeviceType.php
class DeviceType extends Model
{
protected $table = 'device_types';
...
public function _getTotal($status = \Common::STATUS_ACTIVE)
{
return self::where([
'status' => $status
])->count() ?? 0;
}
}
I tried to put this method in the Base Model but I think may not be a good practice. Am I right..?
Is there any way to make this method _getTotal a common method for all the modules..?
You could move this method to a trait and include the trait instead to all classes that need this method.
trait DeviceStatusTotal
{
public function _getTotal($status = \Common::STATUS_ACTIVE)
{
return self::where([
'status' => $status
])->count() ?? 0;
}
}
DeviceType.php
class DeviceType extends Model
{
use DeviceStatusTotal;
protected $table = 'device_types';
// ...
}
Or you can create a classe extending Model default class and your models extends from this custom class (that haves your custom function)
You can use laravel Global scopes:
https://laravel.com/docs/5.7/eloquent#global-scopes
Another why use traits and use in the models and make the method to local scope as:
public function scopePopular($query) {
return $query->where('votes', '>', 100);
}

Laravel 5 Global Mutator to escape all html chars?

Building an API but because I am dynamically creating tables etc in Vue.js from the API response I can't make use of blades html escaping.
I know in my model I can use a mutator:
public function getNameAttribute($value) {
return strtolower($value); // example
}
But we have a lot fields that can be edited across many models. Is there a way I can automatically return all values with htmlspecialchars()?
Or is the only option to change the API responses to run htmlspecialchars() on every field?
Thanks.
EDIT: Using Laravel Spark. Suggested answer was to create a new model and extend that on our models but the Spark models already have a long list of extended classes.
You can create a class which extends Model class and make all you models extend this class instead of Model. In the class override getAttributeValue method:
protected function getAttributeValue($key)
{
$value = $this->getAttributeFromArray($key);
if ($this->hasGetMutator($key)) {
return $this->mutateAttribute($key, $value);
}
if ($this->hasCast($key)) {
return $this->castAttribute($key, $value);
}
if (in_array($key, $this->getDates()) && ! is_null($value)) {
return $this->asDateTime($value);
}
return is_string($value) ? htmlspecialchars($value) : $value;
}
Apart from extending classes. Alternate solution is to use traits.
Create a new trait
namespace App\Traits;
trait ExtendedModel {
public function getNameAttribute($value)
{
return strtolower($value); // example
}
}
Use traits in required models:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Traits\ExtendedModel;
class MyTable extends Model
{
use ExtendedModel;
}
This is how I would do this.
I would make a trait and then attach this trait to all models where you want it.
The trait would overrider getAttributeValue($key) method with your own logic. Like this:
trait CastAttributes {
public function getAttributeValue($value)
{
$value = $this->getAttributeValue($value);
return is_string($value) ? strtolower($this->getAttributeValue($value)) : $val;
}
}
That beign said, you will most likely not want to cast absolutely everything. In that case I would do this:
trait CastAttributes {
protected $toLower = [];
public function getAttributeValue($key)
{
$value = $this->getAttributeValue($key);
return in_array($key, $this->toLower) ? strtolower($value) : $value;
}
}
And then override the $toLower array with all the attributes that you actually want cast to lower case.

Laravel Mutators with Constructor?

Ill have a problem because my mutators never get called when ill use an constructor:
Like this:
function __construct() {
$this->attributes['guid'] = Uuid::generate(4)->string;
}
public function setDateAttribute($date) {
dd($date); // Never gets called
}
Ill already found out, that the mutators would ne be called when ill use an constructor, so i should use:
public function __construct(array $attributes = array()){
parent::__construct($attributes);
$this->attributes['guid'] = Uuid::generate(4)->string;
}
public function setDateAttribute($date) {
dd($date); // now its getting called
}
But so ill get the following error:
array_key_exists() expects parameter 2 to be array, null given
But i dont know where? Can anyone help me out how to create a default value (like a UUID) for a specific column, and use mutators in the same class?
Edit: Thanks Martin Bean for your help, but i am now getting the following error:
Cannot declare class App\Uuid because the name is already in use
I have tried:
Creating a File called "Uuid.php" in /app/ -> /app/Uuid.php
With this content:
<?php namespace App;
use Webpatser\Uuid\Uuid;
trait Uuid
{
public static function bootUuid()
{
static::creating(function ($model) {
$model->uuid = Uuid::generate(4)->string();
});
}
}
Changed my Model to:
<?php namespace App;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
class Task extends Model {
use \App\Uuid;
Thank you very much!
Edit 2:
Ill tried it this way:
class Task extends Model {
protected $table = 'tasks';
protected $fillable = ['..... 'date', 'guid'];
public function setGuidAttribute($first=false){
if($first) $this->attributes['guid'] = Uuid::generate(4)->string;
}
TaskController:
public function store() {
$input = Request::all();
$input['guid'] = true;
Task::create($input);
return redirect('/');
}
Works fine, but when ill use:
public function setDateAttribute(){
$this->attributes['date'] = date('Y-m-d', $date);
}
In Task.php ill get:
Undefined variable: date
EDITED:
based on your comment:
i would like to set a field on first insert
use Uuid; //please reference the correct namespace to Uuid
class User extends Model{
protected $fillable = [
'first_name',
'email',
'guid' //add guid to list of your fillables
]
public function setGuidAttribute($first=false){
if($first) $this->attributes['guid'] = Uuid::generate(4)->string;
}
}
Later:
$user = User::create([
'guid' => true, //setAttribute will handle this
'first_name' => 'Digitlimit',
'email" => my#email.com
]);
dd($user->guid);
NB: Remove the __construct() method from your model
Mutators are called when you try and set a property on the model—they’re invoked via the __get magic method. If you manually assign a property in a method or constructor, then no mutators will ever be called.
Regardless, you should not be creating constructors on Eloquent model classes. This could interfere with how Eloquent models are “booted”.
If you need to set an UUID on a model then I’d suggest using a trait that has its own boot method:
namespace App;
trait Uuid
{
public static function bootUuid()
{
static::creating(function ($model) {
$model->uuid = \Vendor\Uuid::generate(4)->string();
});
}
}
You apply the trait to your model…
class SomeModel extends Model
{
use \App\Uuid;
}
…and now each time a model is created, a UUID will be generated and stored in the database with your model.

Laravel 4 - Model properties' names different than database columns

I have one question, that seems to be logical, but I can't find answer for it.
Let's say I have Model Task:
class Task extends Eloquent {
protected $fillable = array('is_done');
}
So, I have one property is_done, but when working on frontend and backend part of application, I would like to have isDone as model property.
Is there a way to say it to framework, to somehow repack it for me? So that I am able to use isDone, throughout application, and that Model takes care of converting it to is_done, when it comes to saving/updating.
This would help me, so I don't have to think about names specified in database (like when using alias in traditional SQL clauses).
Is this possible at all? Does it make sense?
To prevent writing a getter/setter methods for every single attribute of the model, you can override the magic methods from the Eloquent class to access them in camelCase style:
class Model extends Eloquent {
public function __get($key)
{
$snake_key = snake_case($key);
return parent::__get($snake_key);
}
public function __set($key, $value)
{
$snake_key = snake_case($key);
parent::__set($snake_key, $value);
}
public function __isset($key)
{
$snake_key = snake_case($key);
return parent::__isset($snake_key);
}
public function __unset($key)
{
$snake_key = snake_case($key);
parent::__unset($snake_key);
}
}
Would a getter method for your attribute help you? If yes:
<?php
class Task extends Eloquent {
public function isDone()
{
return $this->getAttribute('is_done');
}
}
If not, and you really need to access $Task->isDone: try to overwrite the $key in magic _get() method for $key == 'isDone' (and maybe other attributes) and return the parent::_get() with $key:
<?php
class Task extends Eloquent {
public function __get($key)
{
if($key == 'isDone')
$key = 'is_done';
return parent::__get($key);
}
}
And perhaps, your Eloquent needs an attribute mapper for the attribute magic methods ;)

Resources