Skip to content

Commit

Permalink
增加模型关联注解
Browse files Browse the repository at this point in the history
  • Loading branch information
yunwuxin committed Mar 4, 2020
1 parent 8df7414 commit 2c8cb77
Show file tree
Hide file tree
Showing 15 changed files with 569 additions and 11 deletions.
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
## 使用方法

~~~
### 路由注解

~~~php
<?php

namespace app\controller;
Expand Down Expand Up @@ -56,6 +58,28 @@ class IndexController

~~~

### 模型注解

~~~php
<?php

namespace app\model;

use think\Model;
use think\annotation\model\InteractsWithAnnotations;
use think\annotation\model\relation\HasMany;

/**
* @HasMany("articles", model=Article::class, foreignKey="user_id")
*/
class User extends Model
{
use InteractsWithAnnotations;

//...
}
~~~

IDE Support
-----------

Expand Down
1 change: 0 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
"email": "448901948@qq.com"
}
],
"minimum-stability": "dev",
"require": {
"topthink/framework": "^6.0",
"doctrine/annotations": "^1.6",
Expand Down
32 changes: 23 additions & 9 deletions src/Service.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Doctrine\Common\Annotations\Reader;
use InvalidArgumentException;
use RecursiveCallbackFilterIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use SplFileInfo;
use think\App;

class Service extends \think\Service
{
Expand All @@ -20,7 +26,15 @@ public function register()
// TODO: this method is deprecated and will be removed in doctrine/annotations 2.0
AnnotationRegistry::registerLoader('class_exists');

$this->reader = new CachedReader(new AnnotationReader(), $this->app);
$this->app->bind(Reader::class, function (App $app) {
return new CachedReader(new AnnotationReader(), $app);
});
}

public function boot(Reader $reader)
{
$this->reader = $reader;

//注解路由
$this->registerAnnotationRoute();

Expand All @@ -35,17 +49,17 @@ public function register()
*/
protected function findClasses($dir)
{
$files = iterator_to_array(new \RecursiveIteratorIterator(
new \RecursiveCallbackFilterIterator(
new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS),
function (\SplFileInfo $current) {
$files = iterator_to_array(new RecursiveIteratorIterator(
new RecursiveCallbackFilterIterator(
new RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS),
function (SplFileInfo $current) {
return '.' !== substr($current->getBasename(), 0, 1);
}
),
\RecursiveIteratorIterator::LEAVES_ONLY
RecursiveIteratorIterator::LEAVES_ONLY
));

usort($files, function (\SplFileInfo $a, \SplFileInfo $b) {
usort($files, function (SplFileInfo $a, SplFileInfo $b) {
return (string) $a > (string) $b ? 1 : -1;
});

Expand All @@ -64,8 +78,8 @@ protected function findClass($file)
$class = false;
$namespace = false;
$tokens = token_get_all(file_get_contents($file));
if (1 === \count($tokens) && T_INLINE_HTML === $tokens[0][0]) {
throw new \InvalidArgumentException(sprintf('The file "%s" does not contain PHP code. Did you forgot to add the "<?php" start tag at the beginning of the file?', $file));
if (1 === count($tokens) && T_INLINE_HTML === $tokens[0][0]) {
throw new InvalidArgumentException(sprintf('The file "%s" does not contain PHP code. Did you forgot to add the "<?php" start tag at the beginning of the file?', $file));
}
for ($i = 0; isset($tokens[$i]); ++$i) {
$token = $tokens[$i];
Expand Down
169 changes: 169 additions & 0 deletions src/model/InteractsWithAnnotations.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
<?php

namespace think\annotation\model;

use Doctrine\Common\Annotations\Reader;
use think\annotation\model\relation\BelongsTo;
use think\annotation\model\relation\BelongsToMany;
use think\annotation\model\relation\HasMany;
use think\annotation\model\relation\HasManyThrough;
use think\annotation\model\relation\HasOne;
use think\annotation\model\relation\HasOneThrough;
use think\annotation\model\relation\MorphByMany;
use think\annotation\model\relation\MorphMany;
use think\annotation\model\relation\MorphOne;
use think\annotation\model\relation\MorphTo;
use think\annotation\model\relation\MorphToMany;
use think\Model;

/**
* Trait InteractsWithAnnotations
* @package think\annotation\model
* @mixin Model
*/
trait InteractsWithAnnotations
{
static protected $annotationRelations = [];

protected function prepareAnnotationRelations()
{
if (empty(self::$annotationRelations)) {

$this->invoke(function (Reader $reader) {
$annotations = $reader->getClassAnnotations(new \ReflectionClass($this));

foreach ($annotations as $annotation) {
switch (true) {
case $annotation instanceof HasOne:
$relation = function (Model $model) use ($annotation) {
return $model->hasOneThrough(
$annotation->model,
$annotation->foreignKey,
$annotation->localKey
);
};
break;
case $annotation instanceof BelongsTo:
$relation = function (Model $model) use ($annotation) {
return $model->belongsTo(
$annotation->model,
$annotation->foreignKey,
$annotation->localKey
);
};
break;
case $annotation instanceof HasMany:
$relation = function (Model $model) use ($annotation) {
return $model->hasMany(
$annotation->model,
$annotation->foreignKey,
$annotation->localKey
);
};
break;
case $annotation instanceof HasManyThrough:
$relation = function (Model $model) use ($annotation) {
return $model->hasManyThrough(
$annotation->model,
$annotation->through,
$annotation->foreignKey,
$annotation->throughKey,
$annotation->localKey,
$annotation->throughPk
);
};
break;
case $annotation instanceof HasOneThrough:
$relation = function (Model $model) use ($annotation) {
return $model->hasOneThrough(
$annotation->model,
$annotation->through,
$annotation->foreignKey,
$annotation->throughKey,
$annotation->localKey,
$annotation->throughPk
);
};
break;
case $annotation instanceof BelongsToMany:
$relation = function (Model $model) use ($annotation) {
return $model->belongsToMany($annotation->model,
$annotation->middle,
$annotation->foreignKey,
$annotation->localKey
);
};
break;
case $annotation instanceof MorphOne:
$relation = function (Model $model) use ($annotation) {
return $model->morphOne(
$annotation->model,
$annotation->morph ?: $annotation->value,
$annotation->type
);
};
break;
case $annotation instanceof MorphMany:
$relation = function (Model $model) use ($annotation) {
return $model->morphMany(
$annotation->model,
$annotation->morph ?: $annotation->value,
$annotation->type
);
};
break;
case $annotation instanceof MorphTo:
$relation = function (Model $model) use ($annotation) {
return $model->morphTo($annotation->morph ?: $annotation->value, $annotation->alias);
};
break;
case $annotation instanceof MorphToMany:
$relation = function (Model $model) use ($annotation) {
return $model->morphToMany(
$annotation->model,
$annotation->middle,
$annotation->morph,
$annotation->localKey
);
};
break;
case $annotation instanceof MorphByMany:
$relation = function (Model $model) use ($annotation) {
return $model->morphByMany(
$annotation->model,
$annotation->middle,
$annotation->morph,
$annotation->foreignKey
);
};
break;
}

if (!empty($relation)) {
static::$annotationRelations[$annotation->value] = $relation;
}
}
});
}
}

protected function isAnnotationRelationMethod($method)
{
return array_key_exists($method, static::$annotationRelations);
}

protected function getAnnotationRelation($method)
{
return (static::$annotationRelations[$method])($this);
}

public function __call($method, $args)
{
$this->prepareAnnotationRelations();
if ($this->isAnnotationRelationMethod($method)) {
return $this->getAnnotationRelation($method);
}

return parent::__call($method, $args);
}
}
28 changes: 28 additions & 0 deletions src/model/relation/BelongsTo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace think\annotation\model\relation;

use Doctrine\Common\Annotations\Annotation;

/**
* @Annotation
* @Annotation\Target({"CLASS"})
*/
final class BelongsTo extends Annotation
{
/**
* @var string
* @Annotation\Required
*/
public $model;

/**
* @var string
*/
public $foreignKey = '';

/**
* @var string
*/
public $localKey = '';
}
33 changes: 33 additions & 0 deletions src/model/relation/BelongsToMany.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace think\annotation\model\relation;

use Doctrine\Common\Annotations\Annotation;

/**
* @Annotation
* @Annotation\Target({"CLASS"})
*/
final class BelongsToMany extends Annotation
{
/**
* @var string
* @Annotation\Required
*/
public $model;

/**
* @var string
*/
public $middle = '';

/**
* @var string
*/
public $foreignKey = '';

/**
* @var string
*/
public $localKey = '';
}
29 changes: 29 additions & 0 deletions src/model/relation/HasMany.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace think\annotation\model\relation;

use Doctrine\Common\Annotations\Annotation;

/**
* @Annotation
* @Annotation\Target({"CLASS"})
*/
final class HasMany extends Annotation
{
/**
* @var string
* @Annotation\Required
*/
public $model;

/**
* @var string
*/
public $foreignKey = '';

/**
* @var string
*/
public $localKey = '';

}
Loading

0 comments on commit 2c8cb77

Please sign in to comment.