PhpStorm won't open a function in the model - laravel

I have a function in the MissingData model, that is called from the MissingDataController:
public function store(Request $request)
{
$missingData = MissingData::where('operation_id', 1)->firstOrFail();
$missingData->fillData();
}
Code works great, but PhpStorm cannot open fillData() when using Ctrl + B. In general, this happens when $missingData is not a MissingData model. But here, when I dd($missingData), it returns a App\Models\MissingData instance.
I also tried to invalidate PhpStorm Cache, but it didn't work.
Why is it happening ? For all other methods, PhpStorm works fine.

I would assume that PHPStorm doesn't know that result of firstOrFail() is of type App\Models\MissingData. To solve that I would simply add phpdoc:
/** #var \App\Models\MissingData $missingData */
$missingData = MissingData::where('operation_id', 1)->firstOrFail();

Related

How to overwrite bulk clone function for laravel backpack

I am trying to overwrite a bulk clone function.
I just want to clone some values, and the rest assign a static value. For example, I just want to clone the name and description values, and the date I want to assign it the current date.
And well I don't know how to do it.
use \Backpack\CRUD\app\Http\Controllers\Operations\BulkCloneOperation { bulkClone as traitBulkClone; }
public function bulkClone($id) {
// your custom code here
$this->traitBulkClone($id);
}
TLDR: The most efficient way would probably be to overwrite the replicate() method on your model. Note that it is not a Backpack method, but an Eloquent method that BulkCloneOperation uses to duplicate a particular entry.
WHY?
Inside the BulkCloneOperation that you're using, you'll notice the route calls the bulkClone() method, that itself is just making some calls to the replicate() method on the model. That means you have two options to override this behaviour:
(Option A). Override the bulkClone() method in your CrudController. This will override the behaviour only on that particular admin operation.
(Option B). Override the replicate() method in you Model. That way, any time replicate() is called (by your admin panel or any other part of your software), the duplication is done in the way you specified.
In most cases, I think Option B is more appropriate, since it would avoid future code duplication. Here's Laravel's replicate() method at this time, just copy-pasting it into your model and modifying it to fit your needs is the best solution, if you ask me:
/**
* Clone the model into a new, non-existing instance.
*
* #param array|null $except
* #return static
*/
public function replicate(array $except = null)
{
$defaults = [
$this->getKeyName(),
$this->getCreatedAtColumn(),
$this->getUpdatedAtColumn(),
];
$attributes = Arr::except(
$this->getAttributes(), $except ? array_unique(array_merge($except, $defaults)) : $defaults
);
return tap(new static, function ($instance) use ($attributes) {
$instance->setRawAttributes($attributes);
$instance->setRelations($this->relations);
$instance->fireModelEvent('replicating', false);
});
}

Routing & pretty URL

I am looking to create prettier URLs, and I'm having issues creating a valid route:
Let's say I have the following page http://localhost/app/account/5/edit.
This works fine with Route::get('account/{account}', 'AccountController#edit');
If I modify the Account Model and modify getRouteKeyName to return 'name', I am able to (with the same Route from above) access the following link: http://localhost/app/account/randomName/edit
The thing is, I am interested in a slightly different route, which is: http://localhost/app/account/randomName-5/edit
If I create a route Route::get('/accounts/{ignore}-{account}/edit', 'AccountController#edit'), it will fail as the first argument sent to edit is string and not an instance of Account. This can easily be fixed by modifying edit(Account $ac) to edit($ignored, Account $ac);... but it feels like cheating.
Is there a way to to get the route to ignore the first {block}?
According to the docs, you can customize your resolution logic for route model binding.
In this scenario, you can do something like this in App\Providers\RouteServiceProvider:
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
parent::boot();
Route::bind('accountNameWithId', function ($value) {
list($accountName, $accountId) = explode('-', $value);
return App\Account::whereKey($accountId)
->where('name', $accountName)
->firstOrFail();
});
}
Then you can redefine your route like this:
Route::get('account/{accountNameWithId}', 'AccountController#edit');

What causes an illegal offset in the makeAlias function of laravel 5.5 within this package implementation?

I am making packages for the first time in Laravel (v5.5). Unfortunately, the Route Model Bindings are giving me trouble. At first, I was receiving the id number raw without the model, and I realized that I had to force the package routes through the 'web' middleware grouping.
When I add the 'web' middleware grouping, I can see that laravel tries to load the model, but I receive an "Illegal offset type in isset or empty" in the first line of the Container->makeAlias method.
I have tried using custom binding loaders defined in the ServiceProvider using Route::model( $class, $closure ), but the error occurs before the loader is called. I've even tried moving this declaration from the ServiceProvider->boot method to the ServiceProvider->register method, just in case it is an order of operations issue.
I have tried adding a binding to the app by calling $this->app->bind inside the ServiceProvider, but this has had no effect.
I'm going to post the relevant code below, and I appreciate any insites that you may have! Thank you in advance!
Some Facts
The route
We are loading the url: http://127.0.0.1/profiles/1/edit for this test.
Operation without bindings
When no bindings or middleware are applied, and the url above is called, I can safely load the model from within the function manually and return the view. This indicates that the package is correctly loaded into the main application container.
The model can load in Tinker
To verify the model and database connection, we use tinker and run the command \Clemence\Profiles\Profile::find(1) to which the following is returned:
=> Clemence\Profiles\Profile {#777
id: 1,
name: "Clemence, Jared",
address: """
554 Christmas Tree Lane\n
Bakersfield, CA 93306
""",
email: "jaredclemence#gmail.com",
phone: "610-360-9558",
created_at: null,
updated_at: null,
deleted_at: null,
}
The stack trace
The Route->performBinding method is called with the following arguments:
[
Closure, /* Presumably the loader */
"1", /* the index that is passed into the route URL */
Route /* A self reference back to the containing object */
]
after this, the Closure is called with
[
"1",
Route
]
from the Closure, Application->make is called, the argument named $class is passed another Closure with the following definition:
Closure {#97 ▼
class: "Clemence\Profiles\ProfilesServiceProvider"
this: ProfilesServiceProvider {#87 …}
parameters: {▶}
}
lastly we end up in Container->makeAlias where the Exception is thrown, makeAlias is called with this Closure:
Closure {#97 ▼
class: "Clemence\Profiles\ProfilesServiceProvider"
this: ProfilesServiceProvider {#87 …}
parameters: {▶}
}
The Files
packages/clemence/profiles/src/routes.php
Route::group(['middleware' => ['web']], function() {
Route::get('/profiles/{profile}/edit', '\Clemence\Profiles\ProfileController#edit');
Route::get('/profiles/create', '\Clemence\Profiles\ProfileController#create');
Route::get('/profiles', '\Clemence\Profiles\ProfileController#showAll');
Route::get('/profiles/{profile}', '\Clemence\Profiles\ProfileController#show');
Route::post('/profiles/create', '\Clemence\Profiles\ProfileController#createWithFormData')->name('profile.create');
Route::put('/profiles/{profile}', '\Clemence\Profiles\ProfileController#updateWithFormData')->name('profile.update');
Route::delete('/profoiles/{profile}', '\Clemence\Profiles\ProfileController#destroy')->name('profile.delete');
Route::get('/roles', '\Clemence\Profiles\RoleController#showAll');
Route::get('/roles/{role_id}', '\Clemence\Profiles\RoleController#show');
Route::get('/roles/create', '\Clemence\Profiles\RoleController#create');
});
packages/clemence/profiles/src/ProfilesServiceProvider.php
namespace Clemence\Profiles;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Route;
class ProfilesServiceProvider extends ServiceProvider {
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot() {
$this->loadRoutesFrom( __DIR__ . '/routes.php' );
if ($this->app->runningInConsole()) {
$this->commands([
\Clemence\Profiles\Commands\InstallProfiles::class,
\Clemence\Profiles\Commands\PrepareEnvironmentForProfiles::class
]);
}
}
/**
* Register the application services.
*
* #return void
*/
public function register() {
$this->app->make(\Clemence\Profiles\ProfileController::class);
$this->app->make(\Clemence\Profiles\RoleController::class);
$this->loadMigrationsFrom(__DIR__ . '/migrations');
$this->loadViewsFrom(__DIR__ . '/views', 'profiles');
}
}
packages/clemence/profile/src/ProfileController.php
namespace Clemence\Profiles;
use App\Http\Controllers\Controller;
use Clemence\Profiles\Profile;
use Illuminate\Http\Request;
use Clemence\Profiles\FormTemplateData;
use Illuminate\Support\Facades\URL;
class ProfileController extends Controller
{
/* ... collapsed ... */
/**
* Show the form for editing the specified resource.
*
* #param \App\Profile $profile
* #return \Illuminate\Http\Response
*/
public function edit(Profile $profile )
{
return dd( $profile );
$formData = new FormTemplateData();
$profile = Profile::find( $profile_id );
$formData->setProfile( $profile );
$formData->setFormActionMethod("POST");
$formData->setDeleteButtonText("Delete Profile");
$formData->setSubmitButtonText( "Update Profile" );
$on_cancel_route_url = $formData->getNamedUrl('profile.delete');
$on_submit_route_url = $formData->getNamedUrl('profile.update');
$formData->setFormCancelUrl($on_cancel_route_url);
$formData->setFormSubmitUrl($on_submit_route_url);
return $this->editForm($formData);
}
/* ... collapsed ... */
}
packages/clemence/profile/src/Profile.php
namespace Clemence\Profiles;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Model;
class Profile extends Model
{
use SoftDeletes;
}
RESOLVED: SOLUTION UNKNOWN
I will post the steps that I took to find the resolution here for those who may find this post in the future. I admit: I have no idea what fixed the problem.
What I did after asking the question
First and foremost, I got some sleep. When I have been coding too long, I find that sleep solves most problems.
On waking, I realized that I have a machine with XDebug installed and I can literally step through the PHP code as it executes, so I booted up that machine and did the following:
a. I moved the project to the local machine and opened it in NetBeans.
b. I placed breakpoints at several places in the middle ware so that I could verify that the system was running.
c. I changed the project config to run "as a website" and to load the url http://localhost:8000/profiles/1/edit as the start point.
d. I clicked 'Debug Program' and stepped through the bootup, the route loading, the model binding, and watched it all happen step-by-step looking for any line of code or parameter that looked out of sorts. I watched as the model was selected. I watched as it was passed the id 1 and asked to build the query. I watched as a collection of Profiles was returned (Oh! how excited I was to see the data I wanted sitting in that array and knowing that it was there but something would eventually block it, and I would find the block and release it!), and I watched as the collection was asked to return the first value. Then I watched as the parameter was set on the route. Lastly, I watched as the command $kernal->terminate() ran....
Needlessly to say, I was very disappointed and confused. How could I be getting an error, if the Kernal is running to completion?
So, I looked at the browser window, and there in the browser window was the very information that I was looking for! The Profile had loaded. I had changed nothing, and the Profile had loaded.
I moved the code back to the original machine, maybe it is an environment setting? I ran the web query without debugging, and it loaded... no error!
So, I honestly don't know what happened. I doubt that getting sleep helped the problem.
Maybe the Netbeans Debugger ran a cleanup service that fixed or patched a bad linkage? I honestly don't know, but I do know that this problem is resolved, so I post this answer in the hopes that it helps someone in the future, and that it saves other people the time of needlessly looking for an unnecessary solution.

Laravel and PhpStorm auto code hint

I have a class that returns a collection:
public function getCustomers() : Collection;
When I loop the result, PhpStorm IDE won't know what's inside the collection.
In .NET/JAVA you would write:
public <List<Customer>> getCustomers();
then IDE will knows what's inside the collection and know how to complete/hint that.
Is there any trick to introduce that to PhpStorm?
It's easy with the docblocks:
/**
* #return Customer[]|Collection
*/
public function getCustomers();
Essentially what you are doing here is telling phpStorm that the function returns an array of Customer objects and the Collection as well.
You can type hint multiple returns separating them with a pipe symbol |

Laravel 5, Method 'findOrFail' not found in class

I'm learning Laravel 5 from the Laracast course: Laravel 5 Fundamentals, using PhpStorm IDE.
I have issue with the static methods of model, such as where(), find(), and findOrFail().
When I use these methods PhpStorm shows:
Method 'findOrFail' not found in class App\Article.
My model is Article, and the method all() works.
How can I solve this?
Now you can use Laravel IDE helper and for properly recognition of Model methods (i.e. paginate, findOrFail) add comment for your model class
/** #mixin \Eloquent */
Thank you for interesting. But I solved, I use query() method:
public function show($id)
{
$article = Article::query()->findOrFail($id);
return view('articles.show', compact('article'));
}
It's because those methods are hitting the magic method __call which phpStorm does not know how to follow. It's really just a shortcut for the following...
$article = new \App\Article;
$query = $article->newQuery();
$query->where('column', 'value')->get();
I had the same problem, it turned at the end that I had forgotten a single quotation mark in show.balde.php when I extended the master page:
#extends('app)
when I finally realized it and fixed it everything worked perfectly:
this worked: $article = Article::findOrFail($id);
and this also worked: $article = Article::query()->findOrFail($id);
I hope this helps (I tried this in Laravel 5.3).

Resources