Skip to content

Commit e513701

Browse files
committed
Add support for protected Attribute accessors
1 parent 79bdd94 commit e513701

File tree

3 files changed

+59
-51
lines changed

3 files changed

+59
-51
lines changed

src/Console/ModelsCommand.php

Lines changed: 55 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -569,31 +569,39 @@ public function getPropertiesFromTable($model)
569569
*/
570570
public function getPropertiesFromMethods($model)
571571
{
572-
$methods = get_class_methods($model);
573-
if ($methods) {
574-
sort($methods);
575-
foreach ($methods as $method) {
576-
$reflection = new \ReflectionMethod($model, $method);
577-
$type = $this->getReturnTypeFromReflection($reflection);
572+
$reflectionClass = new ReflectionClass($model);
573+
$methodReflections = $reflectionClass->getMethods();
574+
if ($methodReflections) {
575+
$methodReflections = array_filter($methodReflections, function ($methodReflection) {
576+
return !(
577+
$methodReflection->getDeclaringClass()->getName() === \Illuminate\Database\Eloquent\Model::class && (
578+
$methodReflection->getName() === 'setClassCastableAttribute' ||
579+
$methodReflection->getName() === 'setEnumCastableAttribute'
580+
)
581+
);
582+
});
583+
sort($methodReflections);
584+
foreach ($methodReflections as $methodReflection) {
585+
$type = $this->getReturnTypeFromReflection($methodReflection);
578586
$isAttribute = is_a($type, '\Illuminate\Database\Eloquent\Casts\Attribute', true);
579587
if (
580-
Str::startsWith($method, 'get') && Str::endsWith(
581-
$method,
588+
Str::startsWith($methodReflection->getName(), 'get') && Str::endsWith(
589+
$methodReflection->getName(),
582590
'Attribute'
583-
) && $method !== 'getAttribute'
591+
) && $methodReflection->getName() !== 'getAttribute'
584592
) {
585593
//Magic get<name>Attribute
586-
$name = Str::snake(substr($method, 3, -9));
594+
$name = Str::snake(substr($methodReflection->getName(), 3, -9));
587595
if (!empty($name)) {
588-
$type = $this->getReturnType($reflection);
596+
$type = $this->getReturnType($methodReflection);
589597
$type = $this->getTypeInModel($model, $type);
590-
$comment = $this->getCommentFromDocBlock($reflection);
598+
$comment = $this->getCommentFromDocBlock($methodReflection);
591599
$this->setProperty($name, $type, true, null, $comment);
592600
}
593601
} elseif ($isAttribute) {
594-
$name = Str::snake($method);
595-
$types = $this->getAttributeReturnType($model, $method);
596-
$comment = $this->getCommentFromDocBlock($reflection);
602+
$name = Str::snake($methodReflection->getName());
603+
$types = $this->getAttributeReturnType($model, $methodReflection);
604+
$comment = $this->getCommentFromDocBlock($methodReflection);
597605

598606
if ($types->has('get')) {
599607
$type = $this->getTypeInModel($model, $types['get']);
@@ -608,65 +616,65 @@ public function getPropertiesFromMethods($model)
608616
$this->setProperty($name, null, true, null, $comment);
609617
}
610618
} elseif (
611-
Str::startsWith($method, 'set') && Str::endsWith(
612-
$method,
619+
Str::startsWith($methodReflection->getName(), 'set') && Str::endsWith(
620+
$methodReflection->getName(),
613621
'Attribute'
614-
) && $method !== 'setAttribute'
622+
) && $methodReflection->getName() !== 'setAttribute'
615623
) {
616624
//Magic set<name>Attribute
617-
$name = Str::snake(substr($method, 3, -9));
625+
$name = Str::snake(substr($methodReflection->getName(), 3, -9));
618626
if (!empty($name)) {
619-
$comment = $this->getCommentFromDocBlock($reflection);
627+
$comment = $this->getCommentFromDocBlock($methodReflection);
620628
$this->setProperty($name, null, null, true, $comment);
621629
}
622-
} elseif (Str::startsWith($method, 'scope') && $method !== 'scopeQuery') {
630+
} elseif (Str::startsWith($methodReflection->getName(), 'scope') && $methodReflection->getName() !== 'scopeQuery') {
623631
//Magic set<name>Attribute
624-
$name = Str::camel(substr($method, 5));
632+
$name = Str::camel(substr($methodReflection->getName(), 5));
625633
if (!empty($name)) {
626-
$comment = $this->getCommentFromDocBlock($reflection);
627-
$args = $this->getParameters($reflection);
634+
$comment = $this->getCommentFromDocBlock($methodReflection);
635+
$args = $this->getParameters($methodReflection);
628636
//Remove the first ($query) argument
629637
array_shift($args);
630638
$builder = $this->getClassNameInDestinationFile(
631-
$reflection->getDeclaringClass(),
639+
$methodReflection->getDeclaringClass(),
632640
get_class($model->newModelQuery())
633641
);
634642
$modelName = $this->getClassNameInDestinationFile(
635-
$reflection->getDeclaringClass(),
636-
$reflection->getDeclaringClass()->getName()
643+
$methodReflection->getDeclaringClass(),
644+
$methodReflection->getDeclaringClass()->getName()
637645
);
638646
$this->setMethod($name, $builder . '|' . $modelName, $args, $comment);
639647
}
640-
} elseif (in_array($method, ['query', 'newQuery', 'newModelQuery'])) {
648+
} elseif (in_array($methodReflection->getName(), ['query', 'newQuery', 'newModelQuery'])) {
641649
$builder = $this->getClassNameInDestinationFile($model, get_class($model->newModelQuery()));
642650

643651
$this->setMethod(
644-
$method,
652+
$methodReflection->getName(),
645653
$builder . '|' . $this->getClassNameInDestinationFile($model, get_class($model))
646654
);
647655

648656
if ($this->write_model_external_builder_methods) {
649657
$this->writeModelExternalBuilderMethods($model);
650658
}
651659
} elseif (
652-
!method_exists('Illuminate\Database\Eloquent\Model', $method)
653-
&& !Str::startsWith($method, 'get')
660+
!method_exists('Illuminate\Database\Eloquent\Model', $methodReflection->getName())
661+
&& !Str::startsWith($methodReflection->getName(), 'get')
654662
) {
655663
//Use reflection to inspect the code, based on Illuminate/Support/SerializableClosure.php
656-
if ($returnType = $reflection->getReturnType()) {
664+
if ($returnType = $methodReflection->getReturnType()) {
657665
$type = $returnType instanceof ReflectionNamedType
658666
? $returnType->getName()
659667
: (string)$returnType;
660668
} else {
661669
// php 7.x type or fallback to docblock
662-
$type = (string)$this->getReturnTypeFromDocBlock($reflection);
670+
$type = (string)$this->getReturnTypeFromDocBlock($methodReflection);
663671
}
664672

665-
$file = new \SplFileObject($reflection->getFileName());
666-
$file->seek($reflection->getStartLine() - 1);
673+
$file = new \SplFileObject($methodReflection->getFileName());
674+
$file->seek($methodReflection->getStartLine() - 1);
667675

668676
$code = '';
669-
while ($file->key() < $reflection->getEndLine()) {
677+
while ($file->key() < $methodReflection->getEndLine()) {
670678
$code .= $file->current();
671679
$file->next();
672680
}
@@ -680,20 +688,20 @@ public function getPropertiesFromMethods($model)
680688
$search = '$this->' . $relation . '(';
681689
if (stripos($code, $search) || ltrim($impl, '\\') === ltrim((string)$type, '\\')) {
682690
//Resolve the relation's model to a Relation object.
683-
$methodReflection = new \ReflectionMethod($model, $method);
684691
if ($methodReflection->getNumberOfParameters()) {
685692
continue;
686693
}
687694

688-
$comment = $this->getCommentFromDocBlock($reflection);
695+
$comment = $this->getCommentFromDocBlock($methodReflection);
689696
// Adding constraints requires reading model properties which
690697
// can cause errors. Since we don't need constraints we can
691698
// disable them when we fetch the relation to avoid errors.
692-
$relationObj = Relation::noConstraints(function () use ($model, $method) {
699+
$relationObj = Relation::noConstraints(function () use ($model, $methodReflection) {
693700
try {
694-
return $model->$method();
701+
$methodName = $methodReflection->getName();
702+
return $model->$methodName();
695703
} catch (Throwable $e) {
696-
$this->warn(sprintf('Error resolving relation model of %s:%s() : %s', get_class($model), $method, $e->getMessage()));
704+
$this->warn(sprintf('Error resolving relation model of %s:%s() : %s', get_class($model), $methodReflection->getName(), $e->getMessage()));
697705

698706
return null;
699707
}
@@ -714,15 +722,15 @@ public function getPropertiesFromMethods($model)
714722
$collectionClass
715723
);
716724
$this->setProperty(
717-
$method,
725+
$methodReflection->getName(),
718726
$collectionClassNameInModel . '|' . $relatedModel . '[]',
719727
true,
720728
null,
721729
$comment
722730
);
723731
if ($this->write_model_relation_count_properties) {
724732
$this->setProperty(
725-
Str::snake($method) . '_count',
733+
Str::snake($methodReflection->getName()) . '_count',
726734
'int|null',
727735
true,
728736
false
@@ -732,7 +740,7 @@ public function getPropertiesFromMethods($model)
732740
} elseif ($relation === 'morphTo') {
733741
// Model isn't specified because relation is polymorphic
734742
$this->setProperty(
735-
$method,
743+
$methodReflection->getName(),
736744
$this->getClassNameInDestinationFile($model, Model::class) . '|\Eloquent',
737745
true,
738746
null,
@@ -741,7 +749,7 @@ public function getPropertiesFromMethods($model)
741749
} else {
742750
//Single model is returned
743751
$this->setProperty(
744-
$method,
752+
$methodReflection->getName(),
745753
$relatedModel,
746754
true,
747755
null,
@@ -1085,10 +1093,10 @@ protected function hasCamelCaseModelProperties()
10851093
return $this->laravel['config']->get('ide-helper.model_camel_case_properties', false);
10861094
}
10871095

1088-
protected function getAttributeReturnType(Model $model, string $method): Collection
1096+
protected function getAttributeReturnType(Model $model, \ReflectionMethod $reflectionMethod): Collection
10891097
{
10901098
/** @var Attribute $attribute */
1091-
$attribute = $model->{$method}();
1099+
$attribute = $reflectionMethod->invoke($model);
10921100

10931101
return collect([
10941102
'get' => $attribute->get ? optional(new \ReflectionFunction($attribute->get))->getReturnType() : null,

tests/Console/ModelsCommand/Attributes/Models/Simple.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
class Simple extends Model
1111
{
12-
public function name(): Attribute
12+
protected function name(): Attribute
1313
{
1414
return new Attribute(
1515
function (?string $name): ?string {
@@ -43,7 +43,7 @@ public function lowercaseName(): Attribute
4343
*
4444
* @return \Illuminate\Database\Eloquent\Casts\Attribute
4545
*/
46-
public function notAnAttribute()
46+
protected function notAnAttribute()
4747
{
4848
return new Attribute(
4949
function (?string $value): ?string {

tests/Console/ModelsCommand/Attributes/__snapshots__/Test__test__1.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
*/
2323
class Simple extends Model
2424
{
25-
public function name(): Attribute
25+
protected function name(): Attribute
2626
{
2727
return new Attribute(
2828
function (?string $name): ?string {
@@ -56,7 +56,7 @@ public function lowercaseName(): Attribute
5656
*
5757
* @return \Illuminate\Database\Eloquent\Casts\Attribute
5858
*/
59-
public function notAnAttribute()
59+
protected function notAnAttribute()
6060
{
6161
return new Attribute(
6262
function (?string $value): ?string {

0 commit comments

Comments
 (0)