PhpStorm is a powerful IDE for PHP development, with many features to write code faster and more efficiently, including refactoring your code. In this tutorial, let's look at some examples, with screenshots.
When working with variables - it often gets hard to name them (naming is hard right?). It sometimes leads to variables being named in a way that is not very descriptive. So you want to rename them to something more meaningful. PhpStorm has a feature that helps you do that:
In this case, our variable is going to be $q
and it's not very descriptive here:
So we want to rename it to something more meaningful. We can do that by right-clicking on the variable name and selecting Refactor > Rename
:
This will even give us suggestions:
We can select one of the suggestions or type in our own name. In this case, we are going to rename it to $query
:
This has a few benefits:
Renaming classes is a bit more involved than renaming variables. But PhpStorm has a feature that helps you do that as well:
We have a class named UserService
:
app/Services/UserService.php
class UserService{ // ...}
And we want to rename it to CustomerService
as it interacts with customers. Normally we'd have to rename the class in all places it is used (for example, in this case, UsersController
). We can do that by right-clicking on the class and selecting Refactor > Rename
:
This will give us a dialog where we can type in the new name:
In this dialog we have a few options:
Search for references
- this will search for all places where the class is used and rename it there as wellSearch in comments and strings
- this will search for all places where the class is mentioned in comments and strings and rename it there as wellRename Class
- this will rename the class in all places where it is usedWe can select all of these options and click OK
:
It did the following actions automatically for us:
Moving classes is a bit more involved than renaming them. But PhpStorm makes it FAST:
We have a class named UserService
and want to move it from app/Services
to app/Services/Customers
. We can do that by simply creating a new directory called Customers
and dragging the file in:
PhpStorm will be smart enough and update the namespace for us:
app/Services/Customers/CustomerService.php
namespace App\Services\Customers; class CustomerService{ // ...}
Along with updating the use
imports:
It might fail in some cases, but it's usually pretty good at it, and I did not have many issues with it. Maybe once in 10 times, it fails to update the namespace correctly. But it's still a lot faster than doing it manually.
Extracting methods is a common refactoring task. It's also a very easy to do in PhpStorm:
We have a method that is doing a lot of things:
app/Controllers/Admin/ClientReportsController.php
$query = Transaction::with('project') ->with('transaction_type') ->with('income_source') ->with('currency') ->orderBy('transaction_date', 'desc'); if ($request->has('project')) { $query->where('project_id', $request->project);} $transactions = $query->get(); $entries = [];foreach ($transactions as $row) { if ($row->transaction_date != null) { $date = Carbon::createFromFormat(config('panel.date_format'), $row->transaction_date)->format('Y-m'); if (! isset($entries[$date])) { $entries[$date] = []; } $currency = $row->currency->code; if (! isset($entries[$date][$currency])) { $entries[$date][$currency] = [ 'income' => 0, 'expenses' => 0, 'fees' => 0, 'total' => 0, ]; } $income = 0; $expenses = 0; $fees = 0; if ($row->amount > 0) { $income += $row->amount; } else { $expenses += $row->amount; } if (! is_null($row->income_source->fee_percent)) { $fees = $row->amount * ($row->income_source->fee_percent / 100); } $total = $income + $expenses - $fees; $entries[$date][$currency]['income'] += $income; $entries[$date][$currency]['expenses'] += $expenses; $entries[$date][$currency]['fees'] += $fees; $entries[$date][$currency]['total'] += $total; }}$projects = Project::pluck('name', 'id')->prepend('--- ' . trans('cruds.clientReport.reports.allProjects') . ' ---', '');if ($request->has('project')) { $currentProject = $request->get('project');} else { $currentProject = '';}
It seems like it's doing a lot of things, and it's hard to understand what it's doing. We can extract a few methods from this. Let's start with the big foreach:
We can right-click on the foreach and select Refactor > Extract > Method
:
After that's done we will have a new method:
app/Controllers/Admin/ClientReportsController.php
public function index(Request $request){ // ... $entries = $this->transformEntries($transactions); // ... } /** * @param \Illuminate\Database\Eloquent\Collection|array $transactions * @return array */private function transformEntries(\Illuminate\Database\Eloquent\Collection|array $transactions): array{ $entries = []; foreach ($transactions as $row) { if ($row->transaction_date != null) { $date = Carbon::createFromFormat(config('panel.date_format'), $row->transaction_date)->format('Y-m'); if (!isset($entries[$date])) { $entries[$date] = []; } $currency = $row->currency->code; if (!isset($entries[$date][$currency])) { $entries[$date][$currency] = [ 'income' => 0, 'expenses' => 0, 'fees' => 0, 'total' => 0, ]; } $income = 0; $expenses = 0; $fees = 0; if ($row->amount > 0) { $income += $row->amount; } else { $expenses += $row->amount; } if (!is_null($row->income_source->fee_percent)) { $fees = $row->amount * ($row->income_source->fee_percent / 100); } $total = $income + $expenses - $fees; $entries[$date][$currency]['income'] += $income; $entries[$date][$currency]['expenses'] += $expenses; $entries[$date][$currency]['fees'] += $fees; $entries[$date][$currency]['total'] += $total; } } return $entries;}
We can quickly spot that the extracted method still contains the same functionality and PhpStorm even added a doc block, param types, and return type for us. We didn't have to do anything else!
Another example here that is a bit more complex:
Extracting this method will result in:
app/Controllers/Admin/ClientReportsController.php
public function index(Request $request){ // ... [$projects, $currentProject] = $this->getProjectInformationFromRequest($request); // ...} /** * @param Request $request * @return array */private function getProjectInformationFromRequest(Request $request): array{ $projects = Project::pluck('name', 'id')->prepend('--- ' . trans('cruds.clientReport.reports.allProjects') . ' ---', ''); if ($request->has('project')) { $currentProject = $request->get('project'); } else { $currentProject = ''; } return [$projects, $currentProject];}
In this example, you can quickly spot that 2 variables are returned from the new function. Yet PhpStorm still handled it correctly! While it's not ideal to return 2 variables that are not related to each other, it's still a good example of how PhpStorm can handle this.
The final code will look like this:
app/Controllers/Admin/ClientReportsController.php
public function index(Request $request){ $query = Transaction::with('project') ->with('transaction_type') ->with('income_source') ->with('currency') ->orderBy('transaction_date', 'desc'); if ($request->has('project')) { $query->where('project_id', $request->project); } $transactions = $query->get(); $entries = $this->transformEntries($transactions); [$projects, $currentProject] = $this->getProjectInformationFromRequest($request); return view('admin.clientReports.index', compact( 'entries', 'projects', 'currentProject' ));} /** * @param Collection|array $transactions * @return array */private function transformEntries(Collection|array $transactions): array{ $entries = []; foreach ($transactions as $row) { if ($row->transaction_date != null) { $date = Carbon::createFromFormat(config('panel.date_format'), $row->transaction_date)->format('Y-m'); if (!isset($entries[$date])) { $entries[$date] = []; } $currency = $row->currency->code; if (!isset($entries[$date][$currency])) { $entries[$date][$currency] = [ 'income' => 0, 'expenses' => 0, 'fees' => 0, 'total' => 0, ]; } $income = 0; $expenses = 0; $fees = 0; if ($row->amount > 0) { $income += $row->amount; } else { $expenses += $row->amount; } if (!is_null($row->income_source->fee_percent)) { $fees = $row->amount * ($row->income_source->fee_percent / 100); } $total = $income + $expenses - $fees; $entries[$date][$currency]['income'] += $income; $entries[$date][$currency]['expenses'] += $expenses; $entries[$date][$currency]['fees'] += $fees; $entries[$date][$currency]['total'] += $total; } } return $entries;} /** * @param Request $request * @return array */private function getProjectInformationFromRequest(Request $request): array{ $projects = Project::pluck('name', 'id')->prepend('--- ' . trans('cruds.clientReport.reports.allProjects') . ' ---', ''); if ($request->has('project')) { $currentProject = $request->get('project'); } else { $currentProject = ''; } return [$projects, $currentProject];}
Our index
method became much cleaner and easier to read. This also separated the logic into smaller chunks that are easier to maintain.
if
StatementsWe write a lot of if
statements in our code. Sometimes we can refactor them to make them more readable and easier to maintain. Let's take a look at this example:
$document = Document::find($id); if ($document) { if ($document->document_file) { $documentFile = $document->document_file->getUrl(); }}
At first glance, it seems like a simple example. But if we look closer we can see that we have 2 nested if
statements. This is a code smell, and we should refactor it. We can do this by extracting the nested if
statement into a new method:
Once an if statement is marked as an error - you can click Alt + Enter
and select Extract 'if' statement
:
Once the action is complete our code will look like this:
if ($document && $document->document_file) { $documentFile = $document->document_file->getUrl();}
While this was a simple example - PhpStorm will warn you about complex ones too! And it will refactor it to be correct for you.
Sometimes we have variables that are only used once in our code. We can refactor them to be inline variables. Let's take a look at this example:
public function retrieveCustomerData(){ $data = Client::query() ->where('country', 'US') ->where('status_id', 2) ->whereNotNull('company') ->get(); return $data;}
In this example, we can see that we have a variable $data
that is only used once. We can refactor it to be an inline variable:
This will result in our code looking like this:
public function retrieveCustomerData(){ return Client::query() ->where('country', 'US') ->where('status_id', 2) ->whereNotNull('company') ->get();}
It simply suggested removing a variable that is not needed and instead returning the value directly via one line.
Since requirements change all the time - we sometimes need to add new parameters to our functions. PhpStorm can add that for us:
public function retrieveCustomerData(){ return Client::query() ->where('country', 'US') ->where('status_id', 2) ->whereNotNull('company') ->get();}
We can quickly spot that we have 2 hard-coded values in our code - country
and status_id
. We can refactor them to be parameters:
This will result in our code looking like this:
public function retrieveCustomerData($country, $statusId){ return Client::query() ->where('country', $country) ->where('status_id', $statusId) ->whereNotNull('company') ->get();}
It's that simple! We can now pass the values to our function, and it will work as expected. Sadly, it didn't copy hard-coded values as defaults
PhpStorm is a powerful tool that can automate a lot of your daily tasks. It helps with file management, spots mistakes, and even suggests you refactor things for cleaner code.