Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

::find and ::first not returning correct model #1609

Open
juliansteenbakker opened this issue Nov 5, 2024 · 4 comments
Open

::find and ::first not returning correct model #1609

juliansteenbakker opened this issue Nov 5, 2024 · 4 comments
Labels

Comments

@juliansteenbakker
Copy link

Versions:

  • ide-helper Version: 3.2.2
  • Laravel Version: 11.30.0
  • PHP Version: 8.3.x

Description:

::find returns Illuminate\Database\Eloquent\Collection|null|TModel
::first returns Illuminate\Database\Eloquent\TValue|null

image

@mfn
Copy link
Collaborator

mfn commented Nov 5, 2024

Probably because \Illuminate\Database\Eloquent\Builder::find transparently supports finding multiple models by their primary key

@pataar
Copy link
Contributor

pataar commented Nov 5, 2024

Actually, it's because generics aren't supported correctly. I tried to fix it on my fork, but it's a bit hard because of the forked phpdoc package that's included.

It should return a TModel, which is defined as template at the top of the Builder class:
image

Wat worked for me (it's far from ideal though), is replacing the @mixin \Eloquent for the @mixin Builder<$this> in each model phpdoc.

This gives the following result:
image

It can be done using this code snippet (requires the GNU sed version):

find app/Models -type f -name '*.php' -exec sed -i 's/@mixin Eloquent/@mixin \\Illuminate\\Database\\Eloquent\\Builder<\$this>/g' {} +
find app/Models -type f -name '*.php' -exec sed -i 's/@mixin \\Eloquent/@mixin \\Illuminate\\Database\\Eloquent\\Builder<\$this>/g' {} +

@DvDty
Copy link

DvDty commented Dec 10, 2024

find and findOrFail can now return a collection.
first is a nullable value, but even after null check, the type isn't completely resolved.

My workaround is using ->sole():

  Model::first()->sole();
  Model::find()->sole();
  Model::findOrFail($id)->sole();

It also works great for places where you can have multiple childs on HasMany relation, but you expect just one:

Auth::user()->roles->sole()->name

https://laravel.com/docs/11.x/collections#method-sole

If there are no elements in the collection that should be returned by the sole method, an \Illuminate\Collections\ItemNotFoundException exception will be thrown. If there is more than one element that should be returned, an \Illuminate\Collections\MultipleItemsFoundException will be thrown.

@akaDJon
Copy link

akaDJon commented Dec 24, 2024

This is my patch for fixing in my projects. more in my fork https://github.com/akaDJon/laravel-ide-helper

---
Index: resources/views/helper.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/resources/views/helper.php b/resources/views/helper.php
--- a/resources/views/helper.php	(revision 59eb26742b387b0f6b5d3aa56ad961b747e68932)
+++ b/resources/views/helper.php	(revision d56ed201f3bb81dfbf8a2e8f25f328b200bb9521)
@@ -49,9 +49,20 @@
 <?php foreach ($namespaces_by_alias_ns as $namespace => $aliases) : ?>
 namespace <?= $namespace == '__root' ? '' : trim($namespace, '\\') ?> {
     <?php foreach ($aliases as $alias) : ?>
+        <?php // IWAmod: Исправление генериков Eloquent ?>
+        <?php if ($alias->getExtendsNamespace() == '\Illuminate\Database\Eloquent'): ?>
+           /**
+            * @template TModel
+            */
+        <?php endif; ?>
         <?= $alias->getClassType() ?> <?= $alias->getShortName() ?> extends <?= $alias->getExtends() ?> {<?php if ($alias->getExtendsNamespace() == '\Illuminate\Database\Eloquent') : ?>
             <?php foreach ($alias->getMethods() as $method) : ?>
-                <?= trim($method->getDocComment('            ')) ?>
+                <?php //trim($method->getDocComment('            ')) ?>
+                <?php // IWAmod: Исправление имен классов в _ide_helper.php и генериков Eloquent ?>
+                <?php $str = trim($method->getDocComment('            ')); ?>
+                <?php $str = str_replace(['\Illuminate\Database\Eloquent\TModel', '\Illuminate\Database\Eloquent\TValue', '\Illuminate\Database\Eloquent\TModelValue', 'TValue'], 'TModel', $str); ?>
+                <?php $str = str_replace(['\Illuminate\Database\Eloquent\static'], 'static', $str); ?>
+                <?= $str ?>
             public static function <?= $method->getName() ?>(<?= $method->getParamsWithDefault() ?>)
             {<?php if ($method->getDeclaringClass() !== $method->getRoot()) : ?>
                 //Method inherited from <?= $method->getDeclaringClass() ?>
Index: src/Console/ModelsCommand.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/Console/ModelsCommand.php b/src/Console/ModelsCommand.php
--- a/src/Console/ModelsCommand.php	(revision 59eb26742b387b0f6b5d3aa56ad961b747e68932)
+++ b/src/Console/ModelsCommand.php	(revision d56ed201f3bb81dfbf8a2e8f25f328b200bb9521)
@@ -1029,7 +1038,9 @@
                 }
             }
 
-            $phpdoc->appendTag(Tag::createInstance('@mixin ' . $eloquentClassNameInModel, $phpdoc));
+            // В модели добавляем генерик @mixin \Eloquent<ClassnameModel>
+            // для того чтобы работал template TModel в _ide_helper.php
+            $phpdoc->appendTag(Tag::createInstance('@mixin ' . $eloquentClassNameInModel . "<{$classname}>", $phpdoc));
         }
 
         if ($this->phpstorm_noinspections) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants