Laravel 5.7 getting all the siblings for each child - laravel

I wonder how to get all the sibling that a child have in laravel model ?
I know i can use this $siblings = \App\Child::all()->where('parent_id', $parent_id)->where('id', $id); to get all the child siblings, but I want to know if I can do the other way or more cleanest way?
So you can call this in the blade view $child->siblings->full_name something like that.
But I wonder how to use it something like this in the model.php
public function parent()
{
return $this->belongsTo(User::class);
}
with only using the belongsTo or hasMany function maybe if it's possible?
Sorry I'm not good with english so I don't know what it's called so I can search it on google.
Edit:: Adding Child.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Carbon\Carbon;
class Child extends Model
{
protected $guarded = [];
protected $dates = [
'birthdate',
'father_birthdate',
'mother_birthdate',
'guardian_birthdate',
];
public function parent()
{
return $this->belongsTo(User::class);
}
// I want to call this function only using $child->siblings (this one will show all the siblings)
// without passing the parent id
// to make it cleaner
public function siblings($parent_id)
{
return $this->all()->where('parent_id', $parent_id);
}
public function getAgeAttribute()
{
return Carbon::parse($this->attributes['birthdate'])->age;
}
public function getFullNameAttribute()
{
return $this->attributes['first_name'] . ' ' . $this->attributes['last_name'];
}
}

There are 2 ways you could do this.
1 creating the below method:
public function sibling()
{
return $this->belongsTo(User::class)->where('parent_id', $this->parent_id);
}
2 using a single Query:
$siblings = \App\Child::where('parent_id', $parent_id)->get();
Hope this helps
Update
A third way is:
public function scopeSiblings($query, $parent_id) {
return $query->where('parent_id', $parent_id)->get();
}

Related

I want make array for eloquent relationship laravel

in Course model this relation are include
public function course_modules()
{
return $this->hasMany(CourseModule::class, 'course_id');
}
public function course_lessons()
{
return $this->hasMany(CourseLesson::class, 'course_id');
}
public function course_contents()
{
return $this->hasMany(CourseContent::class, 'course_id');
}
i want to make a array for hasMany relation like
$hasMany=[
CourseModule::class,
CourseLesson::class
]
I wanted to do this for fun, turned out pretty difficult, but here you go, there are some requirements you need to make sure of, but it gets the job done, I will be using a mix of PHP & Laravel to accomplish this.
Step 1: Make sure your main class has proper return method types. So in your case.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Course extends Model
{
public function course_modules() : HasMany
{
return $this->hasMany(CourseModule::class, 'course_id');
}
public function course_lessons() : HasMany
{
return $this->hasMany(CourseLesson::class, 'course_id');
}
public function course_contents() : HasMany
{
return $this->hasMany(CourseContent::class, 'course_id');
}
}
Step 2: In your controller, you need to use ReflectionClass, would love if someone actually can improve this for learning purposes.
<?php
namespace App\Http\Controllers;
use ReflectionClass;
class CourseController extends Controller
{
public function test(){
//We will build a hasMany array
$hasMany = [];
//here we will use ReflectionClass on our primary class that we want to use.
$reflection = new ReflectionClass(new \App\Models\Course);
//Lets loop thru the methods available (300+ i don't like this part)
foreach($reflection->getMethods() as $method){
//if the method return type is HasMany
if($method->getReturnType() != null && $method->getReturnType()->getName() == 'Illuminate\Database\Eloquent\Relations\HasMany'){
//we grab the method name
$methodName = $method->getName();
//then we finally check for the relatedClass name and add to the array
array_push($hasMany, get_class(($instance = new Course)->$methodName()->getRelated()));
}
}
//lets dump to see the results
dd($hasMany);
}
Results: an array of the classes :D
array:2 [▼
0 => "App\Models\ProgramTest",
1 => "App\Models\ProgramAnotherTest"
]
According to syntax, we are not able do this in Laravel. However, you can use an model mutors to solve this issue.
public function getCourseDetailsAttribute(){
$arr=[
"course_modules"=>$this->course_modules(),
"course_lessions"=>$this->course_lessons(),
];
return $arr;
}
In the Controller you can write like this,
$course=Course::find(1)->course_details;
For more details t;
https://laravel.com/docs/5.7/eloquent-mutators

Laravel - Delete if no relationship exists

Below is the one of the model. I would like to delete a Telco entry only if no other model is referencing it? What is the best method?
namespace App;
use Illuminate\Database\Eloquent\Model;
class Telco extends Model
{
public function operators()
{
return $this->hasMany('App\Operator');
}
public function packages()
{
return $this->hasMany('App\Package');
}
public function topups()
{
return $this->hasMany('App\Topup');
}
public function users()
{
return $this->morphMany('App\User', 'owner');
}
public function subscribers()
{
return $this->hasManyThrough('App\Subscriber', 'App\Operator');
}
}
You can use deleting model event and check if there any related records before deletion and prevent deletion if any exists.
In your Telco model
protected static function boot()
{
parent::boot();
static::deleting(function($telco) {
$relationMethods = ['operators', 'packages', 'topups', 'users'];
foreach ($relationMethods as $relationMethod) {
if ($telco->$relationMethod()->count() > 0) {
return false;
}
}
});
}
$relationships = array('operators', 'packages', 'topups', 'users', 'subscribers');
$telco = Telco::find($id);
$should_delete = true;
foreach($relationships as $r) {
if ($telco->$r->isNotEmpty()) {
$should_delete = false;
break;
}
}
if ($should_delete == true) {
$telco->delete();
}
Well, I know this is ugly, but I think it should work. If you prefer to un-ugly this, just call every relationship attributes and check whether it returns an empty collection (meaning there is no relationship)
If all relationships are empty, then delete!
After seeing the answers here, I don't feel copy pasting the static function boot to every models that need it. So I make a trait called SecureDelete. I put #chanafdo's foreach, inside a public function in SecureDelete.
This way, I can reuse it to models that need it.
SecureDelete.php
trait SecureDelete
{
/**
* Delete only when there is no reference to other models.
*
* #param array $relations
* #return response
*/
public function secureDelete(String ...$relations)
{
$hasRelation = false;
foreach ($relations as $relation) {
if ($this->$relation()->withTrashed()->count()) {
$hasRelation = true;
break;
}
}
if ($hasRelation) {
$this->delete();
} else {
$this->forceDelete();
}
}
}
Add use SecureDelete to the model that needs it.
use Illuminate\Database\Eloquent\Model;
use App\Traits\SecureDelete;
class Telco extends Model
{
use SecureDelete;
public function operators()
{
return $this->hasMany('App\Operator');
}
// other eloquent relationships (packages, topups, etc)
}
TelcoController.php
public function destroy(Telco $telco)
{
return $telco->secureDelete('operators', 'packages', 'topups');
}
In addition, instead of Trait, you can also make a custom model e.g BaseModel.php that extends Illuminate\Database\Eloquent\Model, put the function secureDelete there, and change your models to extends BaseModel.

Laravel scout check if relation is not empty?

namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Laravel\Scout\Searchable;
class Event extends Model
{
protected $table = 'events';
public $timestamps = true;
use Searchable;
use SoftDeletes;
protected $dates = ['deleted_at'];
public function entities()
{
return $this->belongsTo('App\Entity', 'entity_id');
}
public function users()
{
return $this->belongsTo('App\User', 'id');
}
public function events()
{
return $this->belongsTo('App\DirtyEvent', 'id');
}
public function toSearchableArray()
{
$data = $this->toArray();
$data['entities'] = $this->entities->toArray();
return $data;
}
}
This is my model for Event, as you can see I am using toSearchableArray which is Laravel scout function to import 'relations' to algolia. However the problem is that sometimes it is empty. So for example
event id 1 has entity_id 1
but in another example
event id 2 has entity_id = null
How can I modify this function to check if the entities() relation is not empty before putting it into array?
if i understand u correctly this should help. if the relationship does not exist return an empty array and scout won't update the index
public function toSearchableArray()
{
if(is_null($this->entities)){
return [];
}
$this->entities
return $this->toArray();
}
please update foreign_key in relation as this
user_id as foreign_key instead of id
event_id as foreign_key instead of id
public function users()
{
return $this->belongsTo('App\User', 'user_id');
}
public function events()
{
return $this->belongsTo('App\DirtyEvent', 'event_id');
}
I think if load the relation before the toArray().
public function toSearchableArray()
{
$this->entities;
return $this->toArray();
}

Custom attribute crashing on toJSON

I am trying to determine which position the order is in to generate a order id, but this crashes laravel, nothing in the logs, just a 500 error in the browser:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Load extends Model
{
use SoftDeletes;
protected $dates = ['deleted_at'];
protected $guarded = ['id', 'created_at', 'updated_at'];
protected $appends = ['order_no'];
public function workorder()
{
return $this->belongsTo('App\WorkOrder', 'work_order_id');
}
public function getOrderNoAttribute()
{
$count = 1;
foreach ($this->workorder->loads as $load) {
if ($load->id == $this->id) {
break;
}
$count++;
}
return $this->workorder->id . "-" . $count;
}
}
When I changed it to return just an integer it worked, so I am almost certain it is the relation access causing the issue. Is there a way to do this that is better?
Generally while defining calculated attributes, dependence on relationship should be avoided. So one way to achieve what you are trying is (as you mentioned solved) is to get all loads having the same work_orderid and proceed with it.
public function getLoadCountAttribute ()
{
$id = $this->work_order_id;
return static::where('work_order_id', $id)->count();
}
Another way, more logical I guess, would be to define a relationship and eager load
//define a relation in your Load model
public function load_count ()
{
return count($this->workorder->loads)
//-1 if you want to exclude the current load from count
;
}
//Then use Load::with('load_count') to eager load the load_count
//You may also use global scope
Yet another way would be to define a static function on Workorder model, which will accept an id and return the load_count
//Workorder model
public static function getLoadCount($id)
{
$workorder = static::findOrFail($id);
return count($workorder->loads);
}
Hope this helps.

How do I make a function available to several CodeIgniter methods?

CodeIgniter 2.1.2
I have a class that contains two methods, for the purpose of this questions showone and view. The latter returns all items of a small database and can also perform a search. The other one is for permalinks like domain.com/showone/firstname-lastname
<?php
class Pages extends CI_Controller {
public function view($page)
{
//this includes a mysql search
}
public function showone($slug)
{
//abbreviated version:
$query = "SELECT * FROM mytable WHERE slug = '" . $slug . "'";
$result = $this->db->query($query);
if ($result->num_rows() == 0)
{
//here is where I'd like to use the same search that I used in showall
}
else
{
//show the one item
}
}
} //class
?>
So if a user decides to directly enter a URL that doesn't return anything from the database, I would like to direct him to search results instead of showing a 404.
So how do I set up a function searchdatabase($query) to be used by both showone and view?
You can use your controller functions inside your controller:
public function view($page)
{
$this->showone($slug);
}
Define that function in your model, load your model and then call the model->method.
<?php
//Your Model would look something like this.
class Search_Model extends CI_Model {
public function __construct(){
parent::__construct();
}
public function showone($slug)
{
//abbreviated version:
//Its best to use active record for building your queries
$this->db->where->('slug', $slug);
$result = $this->db->get('mytable');
if ($result->num_rows() == 0)
{
//here is where I'd like to use the same search that I used in showall
}
else
{
//show the one item
}
}
} //class
And then in your controller you would do this:
<?php
//Your Controllerwould look something like this.
class Index extends CI_Controller {
public function __construct(){
parent::__construct();
//model will be loaded for each method.
//if you're going to use this model across several controllers
//its best to autoload it, set that in autoload.php under /app/config
$this->load->model('search_model');
}
public function index(){
$searchResults = $this->search_model->showone('slugone');
}
Update
I just realized you are wanting to show all results if no results were returned. In which case you would perform that logic in your model as well..
In your conditional statement you would do the following:
if ($result->num_rows() == 0)
{
return $this->showall();
}
else
{
return $this->view($slug);
}
}
your showall() and view() methods would return $result->result();

Resources