Adding Setters and Getters to Laravel Model - php

If I want an Eloquent Model class to have setters and getters for the sake of implementing an interface does the following approach make sense or is there a 'laravel' approach to the problem
class MyClass extends Model implements someContract
{
public function setFoo($value) {
parent::__set('foo', $value);
return $this;
}
public function getFoo() {
return parent::__get('foo');
}
}

You are probably looking for accessors (getters) and mutators (setters).
Example of an accessor (getter) in Laravel:
public function getFirstNameAttribute($value)
{
return ucfirst($value);
}
Example of a mutator (setter) in Laravel:
public function setFirstNameAttribute($value)
{
$this->attributes['first_name'] = strtolower($value);
}

For new laravel you can do this in model :
/**
* Interact with the user's first name.
*
* #param string $value
* #return \Illuminate\Database\Eloquent\Casts\Attribute
*/
protected function firstName(): Attribute
{
return Attribute::make(
get: fn ($value) => ucfirst($value),
set: fn ($value) => strtolower($value),
);
}
Wanna know more? see at :
https://laravel.com/docs/9.x/eloquent-mutators#defining-a-mutator

Related

How can I get the method of an interface and its return type and param type from an external class?

I want to get int from an external class.
interface FooInterface
{
/**
* #return int
*/
public function getId(): int;
/**
* #param int $id
*/
public function setId(int $id);
}
I would like to know the return type of the getters to dynamically format the value.
I would like to know the parameter type of the setters to dynamically initialize it.
I need to do it this way because the properties are not initialized, because they are of an object type and their real value is in the interfaces.
class Foo extends FooAbstract implements FooInterface
{
use BuilderFoo;// here are the FooInterface methods
protected function getParamTypeValue(string $functionName)
{
$reflectionParams = (new ReflectionFunction($functionName))->getParameters();//local.ERROR: Function setId() does not exist
return $reflectionParams[0]->getType();
}
protected function getReturnTypeValue(string $functionName)
{
return (new ReflectionFunction($functionName))->getReturnType();//local.ERROR: Function getId() does not exist
}
}
But I get the error: "local.ERROR: Function getId() does not exist" and
"local.ERROR: Function setId() does not exist".
The only way to get the return type of a declared method inside an interface is checking that value after implementation of the Interface.
Interfaces are not instatiables are contracts that other classes has to satisfy.
so here is an example.
interface I{
public function test():int;
}
class TestClass implements I {
public function test():int{
return 5;
}
}
$obj= new TestClass;
gettype($obj->test()) //"integer"
I hope this answer your question.

Calling multiple methods of the same class in Laravel

i was always curious how does Laravel's eloquent work and how they pass many methods in the same line of code.
Example: auth()->user()->where(...)->get()
In this example they use both methods "where" and "get"
So i tried creating the following repository:
class SkillKeyRepository
{
protected $model;
/**
* #param SkillKey $model
*/
public function __construct(SkillKey $model)
{
$this->model = $model;
}
public function all(array $column = []): Collection
{
return $this->model::all($column);
}
public function where($column, $attribute): SkillKeyRepository
{
$this->model::where($column, $attribute);
return $this;
}
public function get()
{
return $this->model::get();
}
}
After that in my service i tried the following:
class SkillKeyService
{
use ServiceTrait;
protected $repository;
/**
* #param SkillKeyRepository $repository
*/
public function __construct(SkillKeyRepository $repository)
{
$this->repository = $repository;
}
public function get()
{
dd($this->repository->where('key', 'TagName')->get());
}
The correct result should be a collection of one item. But it's returning all the data from the database and its ignoring the where() funcion and going directly to the get() function.
Problem is you use the model, to create a query and then trying to chain more to the query using the original model add newQuery method
class SkillKeyRepository
{
protected $query;
/**
* #param SkillKey $model
*/
public function __construct(SkillKey $model)
{
$this->query = $model->newQuery();
}
public function all(array $column = []): Collection
{
return $this->query::all($column);
}
public function where($column, $attribute): SkillKeyRepository
{
$this->query::where($column, $attribute);
return $this;
}
public function get()
{
return $this->query::get();
}
}
The problem is you are using the model, to create a query and then trying to chain more to the query using the original model, however this won't work because you need the previously generated query.
The easiest way to do what you want is to actually return the query builder object itself:
class SkillKeyRepository
{
protected $model;
/**
* #param SkillKey $model
*/
public function __construct(SkillKey $model)
{
$this->model = $model;
}
public function all(array $column = []): Collection
{
return $this->model->all($column);
}
public function where($column, $attribute): QueryBuilder
{
return $this->model->newQuery()->where($column, $attribute);
}
}
(new SkillKeyRepository(new SkillKey()))->where('a','b')->get(); // Should work
However you should really take a step back and reconsider what you are doing. Laravel already has the query builder object to do exactly this, why are you trying to re-write it?

PHPDoc different return type for extended classes

I have created my own DB - Model structure which is similar to Laravel. I have been facing with 2 problems.
I have a Model class which all of my models extend it. For example, my User class extends Model. I want to return that get() method return type of class which is extended.
Is this possible?
Class Model extends DB {
/**
* #return AnyClassThatExtended
*/
function get()
{
}
}
Class User extends Model {
function test() {
$user->get(); // I want it to return User type of object
}
}
You should use
private static $instance;
/**
* return static
*/
public function get() {
if (is_null(self::$instance)) {
self::$instance = new static();
}
return self::$instance;
}
because you are returning current class that you are at (if I understand correctly)
It's possible that PHPStorm does not recognize it

General attribute accessor for a model in Laravel?

Is there a way to set up an accessor that is executed for all model attributes? Im using laravel 5.
For example:
class FooBar extends Model
{
/**
* foo accessor
*/
public function getFooAttribute($value){
return doSomething($value);
}
/**
* bar accessor
*/
public function getBarAttribute($value){
return doSomething2($value);
}
/**
* Access all attributes for this model (?)
*/
public function getAllAttributes($value){
return strtoupper($value);
}
}

Laravel 4 - Trouble overriding model's save method

I'm trying to override my Post class's save() method so that I can validate some of the fields that will be saved to the record:
// User.php
<?php
class Post extends Eloquent
{
public function save()
{
// code before save
parent::save();
//code after save
}
}
When I try and run a this method in my unit testing I get the following error:
..{"error":{"type":"ErrorException","message":"Declaration of Post::save() should be compatible with that of Illuminate\\Database\\Eloquent\\Model::save()","file":"\/var\/www\/laravel\/app\/models\/Post.php","line":4}}
Create Model.php class which you will extend in another self-validating models
app/models/Model.php
class Model extends Eloquent {
/**
* Error message bag
*
* #var Illuminate\Support\MessageBag
*/
protected $errors;
/**
* Validation rules
*
* #var Array
*/
protected static $rules = array();
/**
* Validator instance
*
* #var Illuminate\Validation\Validators
*/
protected $validator;
public function __construct(array $attributes = array(), Validator $validator = null)
{
parent::__construct($attributes);
$this->validator = $validator ?: \App::make('validator');
}
/**
* Listen for save event
*/
protected static function boot()
{
parent::boot();
static::saving(function($model)
{
return $model->validate();
});
}
/**
* Validates current attributes against rules
*/
public function validate()
{
$v = $this->validator->make($this->attributes, static::$rules);
if ($v->passes())
{
return true;
}
$this->setErrors($v->messages());
return false;
}
/**
* Set error message bag
*
* #var Illuminate\Support\MessageBag
*/
protected function setErrors($errors)
{
$this->errors = $errors;
}
/**
* Retrieve error message bag
*/
public function getErrors()
{
return $this->errors;
}
/**
* Inverse of wasSaved
*/
public function hasErrors()
{
return ! empty($this->errors);
}
}
Then, adjust your Post model.
Also, you need to define validation rules for this model.
app/models/Post.php
class Post extends Model
{
// validation rules
protected static $rules = [
'name' => 'required'
];
}
Controller method
Thanks to Model class, Post model is automaticaly validated on every call to save() method
public function store()
{
$post = new Post(Input::all());
if ($post->save())
{
return Redirect::route('posts.index');
}
return Redirect::back()->withInput()->withErrors($post->getErrors());
}
This answer is strongly based on Jeffrey Way's Laravel Model Validation package for Laravel 4.
All credits to this man!
How to override Model::save() in Laravel 4.1
public function save(array $options = array())
{
parent::save($options);
}
If you want to overwrite the save() method, it must be identical to the save() method in Model:
<?php
public function save(array $options = array()) {}
And; you can also hook in the save() call with the Model Events:
http://laravel.com/docs/eloquent#model-events

Categories