2018-11-06 16:07:41 +08:00

306 lines
10 KiB
PHP

<?php
namespace Jenssegers\Mongodb\Eloquent;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use Illuminate\Support\Str;
use Jenssegers\Mongodb\Helpers\EloquentBuilder;
use Jenssegers\Mongodb\Relations\BelongsTo;
use Jenssegers\Mongodb\Relations\BelongsToMany;
use Jenssegers\Mongodb\Relations\HasMany;
use Jenssegers\Mongodb\Relations\HasOne;
use Jenssegers\Mongodb\Relations\MorphTo;
trait HybridRelations
{
/**
* Define a one-to-one relationship.
*
* @param string $related
* @param string $foreignKey
* @param string $localKey
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function hasOne($related, $foreignKey = null, $localKey = null)
{
// Check if it is a relation with an original model.
if (!is_subclass_of($related, \Jenssegers\Mongodb\Eloquent\Model::class)) {
return parent::hasOne($related, $foreignKey, $localKey);
}
$foreignKey = $foreignKey ?: $this->getForeignKey();
$instance = new $related;
$localKey = $localKey ?: $this->getKeyName();
return new HasOne($instance->newQuery(), $this, $foreignKey, $localKey);
}
/**
* Define a polymorphic one-to-one relationship.
*
* @param string $related
* @param string $name
* @param string $type
* @param string $id
* @param string $localKey
* @return \Illuminate\Database\Eloquent\Relations\MorphOne
*/
public function morphOne($related, $name, $type = null, $id = null, $localKey = null)
{
// Check if it is a relation with an original model.
if (!is_subclass_of($related, \Jenssegers\Mongodb\Eloquent\Model::class)) {
return parent::morphOne($related, $name, $type, $id, $localKey);
}
$instance = new $related;
list($type, $id) = $this->getMorphs($name, $type, $id);
$localKey = $localKey ?: $this->getKeyName();
return new MorphOne($instance->newQuery(), $this, $type, $id, $localKey);
}
/**
* Define a one-to-many relationship.
*
* @param string $related
* @param string $foreignKey
* @param string $localKey
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function hasMany($related, $foreignKey = null, $localKey = null)
{
// Check if it is a relation with an original model.
if (!is_subclass_of($related, \Jenssegers\Mongodb\Eloquent\Model::class)) {
return parent::hasMany($related, $foreignKey, $localKey);
}
$foreignKey = $foreignKey ?: $this->getForeignKey();
$instance = new $related;
$localKey = $localKey ?: $this->getKeyName();
return new HasMany($instance->newQuery(), $this, $foreignKey, $localKey);
}
/**
* Define a polymorphic one-to-many relationship.
*
* @param string $related
* @param string $name
* @param string $type
* @param string $id
* @param string $localKey
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function morphMany($related, $name, $type = null, $id = null, $localKey = null)
{
// Check if it is a relation with an original model.
if (!is_subclass_of($related, \Jenssegers\Mongodb\Eloquent\Model::class)) {
return parent::morphMany($related, $name, $type, $id, $localKey);
}
$instance = new $related;
// Here we will gather up the morph type and ID for the relationship so that we
// can properly query the intermediate table of a relation. Finally, we will
// get the table and create the relationship instances for the developers.
list($type, $id) = $this->getMorphs($name, $type, $id);
$table = $instance->getTable();
$localKey = $localKey ?: $this->getKeyName();
return new MorphMany($instance->newQuery(), $this, $type, $id, $localKey);
}
/**
* Define an inverse one-to-one or many relationship.
*
* @param string $related
* @param string $foreignKey
* @param string $otherKey
* @param string $relation
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function belongsTo($related, $foreignKey = null, $otherKey = null, $relation = null)
{
// If no relation name was given, we will use this debug backtrace to extract
// the calling method's name and use that as the relationship name as most
// of the time this will be what we desire to use for the relationships.
if (is_null($relation)) {
list($current, $caller) = debug_backtrace(false, 2);
$relation = $caller['function'];
}
// Check if it is a relation with an original model.
if (!is_subclass_of($related, \Jenssegers\Mongodb\Eloquent\Model::class)) {
return parent::belongsTo($related, $foreignKey, $otherKey, $relation);
}
// If no foreign key was supplied, we can use a backtrace to guess the proper
// foreign key name by using the name of the relationship function, which
// when combined with an "_id" should conventionally match the columns.
if (is_null($foreignKey)) {
$foreignKey = Str::snake($relation) . '_id';
}
$instance = new $related;
// Once we have the foreign key names, we'll just create a new Eloquent query
// for the related models and returns the relationship instance which will
// actually be responsible for retrieving and hydrating every relations.
$query = $instance->newQuery();
$otherKey = $otherKey ?: $instance->getKeyName();
return new BelongsTo($query, $this, $foreignKey, $otherKey, $relation);
}
/**
* Define a polymorphic, inverse one-to-one or many relationship.
*
* @param string $name
* @param string $type
* @param string $id
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function morphTo($name = null, $type = null, $id = null)
{
// If no name is provided, we will use the backtrace to get the function name
// since that is most likely the name of the polymorphic interface. We can
// use that to get both the class and foreign key that will be utilized.
if (is_null($name)) {
list($current, $caller) = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
$name = Str::snake($caller['function']);
}
list($type, $id) = $this->getMorphs($name, $type, $id);
// If the type value is null it is probably safe to assume we're eager loading
// the relationship. When that is the case we will pass in a dummy query as
// there are multiple types in the morph and we can't use single queries.
if (is_null($class = $this->$type)) {
return new MorphTo(
$this->newQuery(), $this, $id, null, $type, $name
);
}
// If we are not eager loading the relationship we will essentially treat this
// as a belongs-to style relationship since morph-to extends that class and
// we will pass in the appropriate values so that it behaves as expected.
else {
$class = $this->getActualClassNameForMorph($class);
$instance = new $class;
return new MorphTo(
$instance->newQuery(), $this, $id, $instance->getKeyName(), $type, $name
);
}
}
/**
* Define a many-to-many relationship.
*
* @param string $related
* @param string $collection
* @param string $foreignKey
* @param string $otherKey
* @param string $parentKey
* @param string $relatedKey
* @param string $relation
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function belongsToMany(
$related,
$collection = null,
$foreignKey = null,
$otherKey = null,
$parentKey = null,
$relatedKey = null,
$relation = null
) {
// If no relationship name was passed, we will pull backtraces to get the
// name of the calling function. We will use that function name as the
// title of this relation since that is a great convention to apply.
if (is_null($relation)) {
$relation = $this->guessBelongsToManyRelation();
}
// Check if it is a relation with an original model.
if (!is_subclass_of($related, \Jenssegers\Mongodb\Eloquent\Model::class)) {
return parent::belongsToMany(
$related,
$collection,
$foreignKey,
$otherKey,
$parentKey,
$relatedKey,
$relation
);
}
// First, we'll need to determine the foreign key and "other key" for the
// relationship. Once we have determined the keys we'll make the query
// instances as well as the relationship instances we need for this.
$foreignKey = $foreignKey ?: $this->getForeignKey() . 's';
$instance = new $related;
$otherKey = $otherKey ?: $instance->getForeignKey() . 's';
// If no table name was provided, we can guess it by concatenating the two
// models using underscores in alphabetical order. The two model names
// are transformed to snake case from their default CamelCase also.
if (is_null($collection)) {
$collection = $instance->getTable();
}
// Now we're ready to create a new query builder for the related model and
// the relationship instances for the relation. The relations will set
// appropriate query constraint and entirely manages the hydrations.
$query = $instance->newQuery();
return new BelongsToMany(
$query,
$this,
$collection,
$foreignKey,
$otherKey,
$parentKey ?: $this->getKeyName(),
$relatedKey ?: $instance->getKeyName(),
$relation
);
}
/**
* Get the relationship name of the belongs to many.
*
* @return string
*/
protected function guessBelongsToManyRelation()
{
if (method_exists($this, 'getBelongsToManyCaller')) {
return $this->getBelongsToManyCaller();
}
return parent::guessBelongsToManyRelation();
}
/**
* @inheritdoc
*/
public function newEloquentBuilder($query)
{
return new EloquentBuilder($query);
}
}