Skip to content

Commit 0e93e1b

Browse files
committed
Add support for protected Attribute accessors
1 parent dda200f commit 0e93e1b

File tree

3 files changed

+59
-52
lines changed

3 files changed

+59
-52
lines changed

src/Console/ModelsCommand.php

Lines changed: 55 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -569,101 +569,108 @@ 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);
602+
$name = Str::snake($methodReflection->getName());
603+
$types = $this->getAttributeReturnType($model, $methodReflection);
604+
$comment = $this->getCommentFromDocBlock($methodReflection);
596605

597606
if ($types->has('get')) {
598607
$type = $this->getTypeInModel($model, $types['get']);
599-
$comment = $this->getCommentFromDocBlock($reflection);
600608
$this->setProperty($name, $type, true, null, $comment);
601609
}
602610

603611
if ($types->has('set')) {
604-
$comment = $this->getCommentFromDocBlock($reflection);
605612
$this->setProperty($name, null, null, true, $comment);
606613
}
607614
} elseif (
608-
Str::startsWith($method, 'set') && Str::endsWith(
609-
$method,
615+
Str::startsWith($methodReflection->getName(), 'set') && Str::endsWith(
616+
$methodReflection->getName(),
610617
'Attribute'
611-
) && $method !== 'setAttribute'
618+
) && $methodReflection->getName() !== 'setAttribute'
612619
) {
613620
//Magic set<name>Attribute
614-
$name = Str::snake(substr($method, 3, -9));
621+
$name = Str::snake(substr($methodReflection->getName(), 3, -9));
615622
if (!empty($name)) {
616-
$comment = $this->getCommentFromDocBlock($reflection);
623+
$comment = $this->getCommentFromDocBlock($methodReflection);
617624
$this->setProperty($name, null, null, true, $comment);
618625
}
619-
} elseif (Str::startsWith($method, 'scope') && $method !== 'scopeQuery') {
626+
} elseif (Str::startsWith($methodReflection->getName(), 'scope') && $methodReflection->getName() !== 'scopeQuery') {
620627
//Magic set<name>Attribute
621-
$name = Str::camel(substr($method, 5));
628+
$name = Str::camel(substr($methodReflection->getName(), 5));
622629
if (!empty($name)) {
623-
$comment = $this->getCommentFromDocBlock($reflection);
624-
$args = $this->getParameters($reflection);
630+
$comment = $this->getCommentFromDocBlock($methodReflection);
631+
$args = $this->getParameters($methodReflection);
625632
//Remove the first ($query) argument
626633
array_shift($args);
627634
$builder = $this->getClassNameInDestinationFile(
628-
$reflection->getDeclaringClass(),
635+
$methodReflection->getDeclaringClass(),
629636
get_class($model->newModelQuery())
630637
);
631638
$modelName = $this->getClassNameInDestinationFile(
632-
$reflection->getDeclaringClass(),
633-
$reflection->getDeclaringClass()->getName()
639+
$methodReflection->getDeclaringClass(),
640+
$methodReflection->getDeclaringClass()->getName()
634641
);
635642
$this->setMethod($name, $builder . '|' . $modelName, $args, $comment);
636643
}
637-
} elseif (in_array($method, ['query', 'newQuery', 'newModelQuery'])) {
644+
} elseif (in_array($methodReflection->getName(), ['query', 'newQuery', 'newModelQuery'])) {
638645
$builder = $this->getClassNameInDestinationFile($model, get_class($model->newModelQuery()));
639646

640647
$this->setMethod(
641-
$method,
648+
$methodReflection->getName(),
642649
$builder . '|' . $this->getClassNameInDestinationFile($model, get_class($model))
643650
);
644651

645652
if ($this->write_model_external_builder_methods) {
646653
$this->writeModelExternalBuilderMethods($model);
647654
}
648655
} elseif (
649-
!method_exists('Illuminate\Database\Eloquent\Model', $method)
650-
&& !Str::startsWith($method, 'get')
656+
!method_exists('Illuminate\Database\Eloquent\Model', $methodReflection->getName())
657+
&& !Str::startsWith($methodReflection->getName(), 'get')
651658
) {
652659
//Use reflection to inspect the code, based on Illuminate/Support/SerializableClosure.php
653-
if ($returnType = $reflection->getReturnType()) {
660+
if ($returnType = $methodReflection->getReturnType()) {
654661
$type = $returnType instanceof ReflectionNamedType
655662
? $returnType->getName()
656663
: (string)$returnType;
657664
} else {
658665
// php 7.x type or fallback to docblock
659-
$type = (string)$this->getReturnTypeFromDocBlock($reflection);
666+
$type = (string)$this->getReturnTypeFromDocBlock($methodReflection);
660667
}
661668

662-
$file = new \SplFileObject($reflection->getFileName());
663-
$file->seek($reflection->getStartLine() - 1);
669+
$file = new \SplFileObject($methodReflection->getFileName());
670+
$file->seek($methodReflection->getStartLine() - 1);
664671

665672
$code = '';
666-
while ($file->key() < $reflection->getEndLine()) {
673+
while ($file->key() < $methodReflection->getEndLine()) {
667674
$code .= $file->current();
668675
$file->next();
669676
}
@@ -677,20 +684,20 @@ public function getPropertiesFromMethods($model)
677684
$search = '$this->' . $relation . '(';
678685
if (stripos($code, $search) || ltrim($impl, '\\') === ltrim((string)$type, '\\')) {
679686
//Resolve the relation's model to a Relation object.
680-
$methodReflection = new \ReflectionMethod($model, $method);
681687
if ($methodReflection->getNumberOfParameters()) {
682688
continue;
683689
}
684690

685-
$comment = $this->getCommentFromDocBlock($reflection);
691+
$comment = $this->getCommentFromDocBlock($methodReflection);
686692
// Adding constraints requires reading model properties which
687693
// can cause errors. Since we don't need constraints we can
688694
// disable them when we fetch the relation to avoid errors.
689-
$relationObj = Relation::noConstraints(function () use ($model, $method) {
695+
$relationObj = Relation::noConstraints(function () use ($model, $methodReflection) {
690696
try {
691-
return $model->$method();
697+
$methodName = $methodReflection->getName();
698+
return $model->$methodName();
692699
} catch (Throwable $e) {
693-
$this->warn(sprintf('Error resolving relation model of %s:%s() : %s', get_class($model), $method, $e->getMessage()));
700+
$this->warn(sprintf('Error resolving relation model of %s:%s() : %s', get_class($model), $methodReflection->getName(), $e->getMessage()));
694701

695702
return null;
696703
}
@@ -714,15 +721,15 @@ public function getPropertiesFromMethods($model)
714721
$collectionClass
715722
);
716723
$this->setProperty(
717-
$method,
724+
$methodReflection->getName(),
718725
$collectionClassNameInModel . '|' . $relatedModel . '[]',
719726
true,
720727
null,
721728
$comment
722729
);
723730
if ($this->write_model_relation_count_properties) {
724731
$this->setProperty(
725-
Str::snake($method) . '_count',
732+
Str::snake($methodReflection->getName()) . '_count',
726733
'int|null',
727734
true,
728735
false
@@ -735,7 +742,7 @@ public function getPropertiesFromMethods($model)
735742
) {
736743
// Model isn't specified because relation is polymorphic
737744
$this->setProperty(
738-
$method,
745+
$methodReflection->getName(),
739746
$this->getClassNameInDestinationFile($model, Model::class) . '|\Eloquent',
740747
true,
741748
null,
@@ -744,7 +751,7 @@ public function getPropertiesFromMethods($model)
744751
} else {
745752
//Single model is returned
746753
$this->setProperty(
747-
$method,
754+
$methodReflection->getName(),
748755
$relatedModel,
749756
true,
750757
null,
@@ -1105,10 +1112,10 @@ protected function hasCamelCaseModelProperties()
11051112
return $this->laravel['config']->get('ide-helper.model_camel_case_properties', false);
11061113
}
11071114

1108-
protected function getAttributeReturnType(Model $model, string $method): Collection
1115+
protected function getAttributeReturnType(Model $model, \ReflectionMethod $reflectionMethod): Collection
11091116
{
11101117
/** @var Attribute $attribute */
1111-
$attribute = $model->{$method}();
1118+
$attribute = $reflectionMethod->invoke($model);
11121119

11131120
return collect([
11141121
'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 {
@@ -29,7 +29,7 @@ function (?string $name): ?string {
2929
*
3030
* @return \Illuminate\Database\Eloquent\Casts\Attribute
3131
*/
32-
public function notAnAttribute()
32+
protected function notAnAttribute()
3333
{
3434
return new Attribute(
3535
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
@@ -20,7 +20,7 @@
2020
*/
2121
class Simple extends Model
2222
{
23-
public function name(): Attribute
23+
protected function name(): Attribute
2424
{
2525
return new Attribute(
2626
function (?string $name): ?string {
@@ -40,7 +40,7 @@ function (?string $name): ?string {
4040
*
4141
* @return \Illuminate\Database\Eloquent\Casts\Attribute
4242
*/
43-
public function notAnAttribute()
43+
protected function notAnAttribute()
4444
{
4545
return new Attribute(
4646
function (?string $value): ?string {

0 commit comments

Comments
 (0)