Skip to content

Instantly share code, notes, and snippets.

@pionl
Last active February 29, 2020 13:08
Show Gist options
  • Save pionl/fae06d53e6e9a66ca18d to your computer and use it in GitHub Desktop.
Save pionl/fae06d53e6e9a66ca18d to your computer and use it in GitHub Desktop.
Model join trait with Automatic Join on Laravel 5 Eloquent Models with relations setup. With automatic filling relation object if detected
<?php
namespace App\Traits\Models;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Query\Expression;
use Illuminate\Support\Str;
/**
* Class ModelJoinTrait
*
* Trait to create model join for scope with detection of model in the attributes.
*
* Prefils the relations array
*
* @package App\Traits\Models
*/
trait ModelJoinTrait
{
protected $modelJoined = array();
/**
* This determines the foreign key relations automatically to prevent the need to figure out the columns.
*
* Based on http://laravel-tricks.com/tricks/automatic-join-on-eloquent-models-with-relations-setup
*
* @param \Illuminate\Database\Query\Builder $query
* @param string $relation_name the function that return the relation
* @param string $operatorOrCollumns ON condition operator
* @param string $type join type (left, right, '', etc)
* @param bool $where custom where condition
* @param array $collumns if you will not pass collumns, it will retreive the collumn listing via
* all collumns *
*
* @return \Illuminate\Database\Query\Builder
*
* @link http://laravel-tricks.com/tricks/automatic-join-on-eloquent-models-with-relations-setup
*/
public function scopeModelJoin($query, $relation_name, $operatorOrCollumns = '=', $type = 'left',
$where = false, $collumns = array()) {
$relation = $this->$relation_name();
$table = $relation->getRelated()->getTable();
$one = $relation->getQualifiedParentKeyName();
$two = $relation->getForeignKey();
// if the operator collumns are in
if (is_array($operatorOrCollumns)) {
$collumns = $operatorOrCollumns;
$operatorOrCollumns = "=";
}
// if the query has missing collumns list, we need to add select for all collumns
// of this table
if (empty($query->columns)) {
$query->select($this->getTable().".*");
}
// if there is no specific collumns, lets get all
if (empty($collumns)) {
$collumns = \Schema::getColumnListing($table);
}
// build the table values prefixed by the table to ensure unique values
foreach ($collumns as $related_column) {
$query->addSelect(new Expression("`$table`.`$related_column` AS `$table.$related_column`"));
}
$this->modelJoined[$table] = $relation;
return $query->join($table, $one, $operatorOrCollumns, $two, $type, $where);
}
/**
* Overides the basic attributes filling with check if the attributes has
* collumns with table format. Checks if we can make a model based on table prefix and
* relation definition. Tested on BelonstTo and left join
*
* @param array $attributes
* @param bool|false $sync
* @return mixed
*/
public function setRawAttributes(array $attributes, $sync = false)
{
// find
$tableAttributes = $this->getAttributesByTablePrefix($attributes);
if (!empty($tableAttributes)) {
foreach ($tableAttributes as $tableFull => $newAttributes) {
// check if its relation function. The table names can
// be in plurar
$table = Str::singular($tableFull);
// check if exists
if (method_exists($this, $table)) {
$relation = $this->$table();
if (is_object($relation) && method_exists($relation, "getRelated")) {
$instance = $relation->getRelated()->newInstance($newAttributes, true);
$this->relations[$table] = $instance;
foreach ($newAttributes as $key => $attribute) {
unset($attributes[$key]);
}
}
}
}
}
return parent::setRawAttributes($attributes, $sync);
}
/**
* Loops all the attributes and finds only values that have prefix format
* TABLE.COLLUMN
* @param array $attributes
* @return array
*/
public function getAttributesByTablePrefix(array $attributes)
{
$tableAttributes = array();
foreach ($attributes as $attribute => $value) {
// check prefix format
if (preg_match("/([^\.]+)\.(.*)/", $attribute, $matches)) {
$tableAttributes[$matches[1]][$matches[2]] = $value;
}
}
return $tableAttributes;
}
}
@tarik02
Copy link

tarik02 commented Jan 25, 2017

How to use this in many to many relations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment