Laravel Eloquent, "inheritance" override fields from parent - laravel

Is there any way in Eloquent to have a model which has some sort of parent model, where both have an identical field, nullable on the child. And if I get the value $child->field I get the childs value if it's not null, otherwise I get the parent value? Something like this:
$parent = new Parent();
$parent->info = 'parent';
$parent->save();
$child = new Child();
$child->info = 'child';
$child->parent()->associate($parent);
$child->save();
echo $child->info
Prints 'child'
And opposite:
$parent = new Parent();
$parent->info = 'parent';
$parent->save();
$child = new Child();
$child->parent()->associate($parent);
$child->info = null;
$child->save();
echo $child->info
Prints 'parent'
It must be a pattern somewhere to have one table rows values 'overrule' another, I just can't seem to find what to search for.

You simply need a custom accessor on the model of your choice:
class Child
{
public function getInfoAttribute($value)
{
if ($value === null) {
return $this->parent->info;
}
return $value;
}
}
This will allow you to still access the property via $child->info.
Please be aware that this will not cast the attribute value according to the $casts array. If you need this casting logic as well, you should use a custom getter method instead of the magic accessor:
class Child
{
public function getInfo()
{
$info = $this->info;
if ($info === null) {
$info = $this->parent->info;
}
return $info;
}
}
If you need to multiple properties, you can either duplicate the code and put it into a trait to remove the clutter from your model. Or instead, you can try overriding the magic __get($key) method:
class Child
{
$parentInheritedAttributes = ['info', 'description'];
// solution 1: using normal model properties like $child->info
public function __get($key)
{
if (in_array($key, $this->parentInheritedAttributes)) {
$value = parent::__get($key);
if ($value === null) {
// this will implicitely use __get($key) of parent
$value = $this->parent->$key;
}
return $value;
}
return parent::__get($key);
}
// solution 2: using getters like $child->getInfo()
public function __call($method, $parameters)
{
if (\Illuminate\Support\Str::startsWith($method, 'get')) {
$attribute = \Illuminate\Support\Str::snake(lcfirst(substr($method, 3)));
in_array($attribute, $this->parentInheritedAttributes)) {
$value = $this->$attribute;
if ($value === null) {
$value = $this->parent->$attribute;
}
return $value;
}
}
}
}

Related

Foreach Storing last data form array in Laravel. Help Plz

I have Tag table, Post Table and PostHasTag Table. But when I go to store my tags in, PostHasTag with post_id and tag_id.
I am using foreach loop for storing tags, but it just stored last tag. Now where is my problem?
public static function postHasTags($post_id, $tag_ids)
{
$store = new PostHasTag();
foreach ($tag_ids as $tag_id) {
$checkTag = PostHasTag::where('post_id', $post_id)->where('tag_id', $tag_id)->get();
if (count($checkTag) > 0) {
return back()->with('error', 'Tag already exists');
} else{
$store->post_id = $post_id;
$store->tag_id = $tag_id;
$store->save();
}
}
return $store;
}
Line $store = new PostHasTag(); should be inside foreach() loop, so it should look like this :
public static function postHasTags($post_id, $tag_ids)
{
foreach ($tag_ids as $tag_id) {
$checkTag = PostHasTag::where('post_id', $post_id)->where('tag_id', $tag_id)->get();
if (count($checkTag)) {
return back()->with('error', 'Tag already exists');
} else{
$store = new PostHasTag(); // Better to put it here
$store->post_id = $post_id;
$store->tag_id = $tag_id;
$store->save();
}
}
return $store;
}
But in this case you are returning only last created PostHasTag. If you need all of them, then push each PostHasTag in a collection and return it.

Laravel How can I have if statements in a factory?

Currently trying to have my factory only fill in certain columns based on the product type. Is it even possible to do this in the first place? Thanks in advance.
Here is my code:
class ProductFactory extends Factory
{
protected $model = Product::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
$productType = ProductType::all();
$products = [[
'type_id'=>$productType->random()->id,
'name'=>$this->faker->catchPhrase,
'description'=>$this->faker->paragraph (2, true),
'price'=>$this->faker->randomFloat(2,5,20),
'stock'=>$this->faker->numberBetween(1,100),
]];
foreach($products as $key=>$product){
if($product['type_id'] = 1){
$product[$key]['pagelength'] = $this->faker->randomFloat(3,100,500);
}
elseif($product['type_id'] = 2){
$product[$key]['playlength']=$this->faker->randomFloat(2,40,140);
}
if($product['type_id'] = 3){
$product[$key]['pegi']=$this->faker->randomFloat(1,1,10);
}
}
}
}
The definition method in a factory is responsible to create a single record/instance/array of the model. So the definition shouldn't have a collection $products
Try the below which I guess will help you achieve what you want
class ProductFactory extends Factory
{
protected $model = Product::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
$productTypes = ProductType::all();
$productType = $productTypes->random()->id;
$product = [
'type_id'=>$productType,
'name'=>$this->faker->catchPhrase,
'description'=>$this->faker->paragraph (2, true),
'price'=>$this->faker->randomFloat(2,5,20),
'stock'=>$this->faker->numberBetween(1,100),
];
if($productType === 1) {
$product['pagelength'] = $this->faker->randomFloat(3,100,500);
}
if($productType === 2) {
$product['playlength'] = $this->faker->randomFloat(2,40,140);
}
if($productType === 3) {
$product['pegi'] = $this->faker->randomFloat(1,1,10);
}
return $product;
}
}

Dropdown of available statuses

I'm trying to figue out what I"m doing wrong to get the error message. I have my User model that has a status_id field and it is a foreign key to my statuses table with an id and name field.
public function scopeAvailable($query, $current = null)
{
$options = $this->getAvailableOptions($current)->toArray();
return $query->whereIn('name', $options);
}
public function getAvailableOptions(string $current = null)
{
$options = collect(['Active', 'Inactive']);
switch ($current) {
case 'Active':
return $options->merge(['Fired', 'Suspended', 'Retired']);
case 'Injured':
return $options->merge(['Fired', 'Retired']);
case 'Suspended':
return $options->merge(['Suspended', 'Retired']);
}
return $options;
}
public function availableStatuses()
{
$status = $this->status ? $this->status->name : null;
return UserStatus::available($status)->get();
}
Type error: Argument 1 passed to App\Models\UserStatus::getAvailableOptions() must be of the type string or null, object given, called in /home/vagrant/projects/app/app/Models/UserStatus.php on line 45

Laravel if statement with other variable value

I try to get data from a model, but the value of the column name from the data model is in another variable. Here's a little preview what I try to achieve:
switch($device->target_objectclass_id) {
case 10:
$handler = Servers::findOrFail($device->target_object_id);
default:
break;
}
if($handler->($condition->column) ($condition->condition) ($condition->value)) {
//process the other data it it's true
}
Example of what should be displayed:
if($handler->status == 1) {
//handle data
}
The reason behide this is a little bit complicated. The user's need to create triggers which will be executed.
Btw, all possible conditons are possible.
For example:
check table where column condition value
check servers where status == 1
Hope someone has an answer if you can understand my problem...
I'm not sure how feasible it is to dynamically insert the operator into an expression like that. However, with a limited set of operators, you could do something like this:
class YourClass
{
public function yourMethod()
{
// Your model instance to test, I've just used a stdClass as an example
$instance = new stdClass;
$instance->status = 1;
// Your condition instance
$condition = new stdClass;
$condition->column = 'status';
$condition->condition = '==';
$condition->value = '1';
if ($this->compare($instance, $condition)) {
// This code will execute then $instance->status == '1'
}
}
public function compare($instance, $condition)
{
$operator = $condition->condition;
$condition = $condition->column;
$value = $condition->value;
switch ($operator) {
case '==':
return $instance->$column == $value;
case '!=':
return $instance->$column != $value;
case '>':
return $instance->$column > $value;
case '<':
return $instance->$column < $value;
case '>=':
return $instance->$column >= $value;
case '<=':
return $instance->$column <= $value;
default:
throw new \Exception('Unsupported operator');
}
}
}
Or a nicer, class based way of doing it...
class Conditional
{
protected $operator;
protected $column;
protected $value;
protected $supportedOperators = [
'==' => 'equals',
'!=' => 'notEquals'
];
public function __construct($column, $operator, $value)
{
$this->column = $column;
$this->operator = $operator;
$this->value = $value;
}
public function check($instance)
{
$method = $this->getMethod();
$instanceValue = $instance->{$this->column};
return $this->$method($instanceValue, $this->value);
}
private function getMethod()
{
if (isset($this->supportedOperators[$this->operator]) && method_exists($this, $this->supportedOperators[$this->operator])) {
return $this->supportedOperators[$this->operator];
}
throw new \Exception('Unsupported operator');
}
protected function equals($one, $two)
{
return $one == $two;
}
protected function notEquals($one, $two)
{
return $one != $two;
}
}
Then you can use it like this...
$instance = new stdClass;
$instance->status = 1;
$conditional = new Conditional('status', '==', 1);
if ($conditional->check($instance)) {
// Execute if $instance->status == 1.
}

Laravel "same" code different results

I'm trying to guess what's different in this short code but I can't. One works an the other one returns:
Undefined property: Illuminate\Database\Eloquent\Builder::$join_books
First method, this one throws error:
public function selectBook(){
try{
BookUser::create(Input::all());
if(!BookUser::userJoinBooks()){
$user = User::eloquentUser();
$user->join_books = 1;
$user->save();
}
MyHelpers::sendSessionFlashMessages('El libro ha sido seleccionado', 'bg-success');
}
catch (Exception $e) {
$message = MyHelpers::exceptionCatch($e);
MyHelpers::sendSessionFlashMessages($message['message'], $message['css-class']);
}
return Redirect::back();
}
Two lines after this one I have the unselect method, looks like the same, but works fine:
public function unselectBook(){
try{
$book_user = BookUser::find(Input::get('id'));
$book_user->delete();
if(BookUser::userBooksCount() == 0){
$user = User::eloquentUser();
$user->join_books = 0;
$user->save();
}
MyHelpers::sendSessionFlashMessages('El libro ha sido eliminado', 'bg-success');
}
catch (Exception $e) {
$message = MyHelpers::exceptionCatch($e);
MyHelpers::sendSessionFlashMessages($message['message'], $message['css-class']);
}
return Redirect::back();
}
If I remove from SelectBook method "!BookUser::userJoinBooks()" in the if statement and use if(1) for example, it work's. I dont undestand why if I make the $user asigment after this static method it could be influence it.
This is the static method code:
public static function userJoinBooks($id = null){
$user_id = $id === null ? Sentry::getUser()->getId(): $id;
return User::where('id',$user_id)->join_books == 0 ? false : true;
}
And the other one too, may be help:
public static function userBooksCount($id = null){
$user_id = $id === null ? Sentry::getUser()->getId(): $id;
return BookUser::where('user_id',$user_id)->count();
}
And this is the eloquentUser method:
public static function eloquentUser(){
$sentry_user = Sentry::getUser();
return $user = User::findOrFail($sentry_user->id);
}
Really a can't remenber why I use this method, I soppose not to use Sentry::getUser in my code that if at some point I will use another user's library.
Thanks :)
The problem is this part of your code:
return User::where('id',$user_id)->join_books == 0 ? false : true;
I suppose you would want to use something like:
return User::findOrFail($user_id)->join_books == 0 ? false : true;

Resources