Skip to content

Commit

Permalink
Split sections into seperate file
Browse files Browse the repository at this point in the history
  • Loading branch information
RhysLees committed Jan 7, 2022
1 parent 4fa3046 commit 8e53156
Show file tree
Hide file tree
Showing 15 changed files with 4,699 additions and 4,715 deletions.
61 changes: 61 additions & 0 deletions Api.md
@@ -0,0 +1,61 @@
## API

⬆️ [Go to main menu](README.md#laravel-tips) ⬅️ [Previous (Log and debug)](Log-and-Debug.md) ➡️ [Next (Other)](Other.md)

- [API Resources: With or Without "data"?](#api-resources-with-or-without-data)
- [API Return "Everything went ok"](#api-return-everything-went-ok)
- [Avoid N+1 queries in API resources](#avoid-N1-queries-in-API-resources)

### API Resources: With or Without "data"?

If you use Eloquent API Resources to return data, they will be automatically wrapped in 'data'. If you want to remove it, add `JsonResource::withoutWrapping();` in `app/Providers/AppServiceProvider.php`.

```php
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
JsonResource::withoutWrapping();
}
}
```

Tip given by [@phillipmwaniki](https://twitter.com/phillipmwaniki/status/1445230637544321029)

### API Return "Everything went ok"

If you have API endpoint which performs some operations but has no response, so you wanna return just "everything went ok", you may return 204 status code "No
content". In Laravel, it's easy: `return response()->noContent();`.

```php
public function reorder(Request $request)
{
foreach ($request->input('rows', []) as $row) {
Country::find($row['id'])->update(['position' => $row['position']]);
}

return response()->noContent();
}
```

### Avoid N+1 queries in API resources
You can avoid N+1 queries in API resources by using the `whenLoaded()` method.<br>
This will only append the department if it’s already loaded in the Employee model.<br>
Without `whenLoaded()` there is always a query for the department
```php
class EmplyeeResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->uuid,
'fullName' => $this->full_name,
'email' => $this->email,
'jobTitle' => $this->job_title,
'department' => DepartmentResource::make($this->whenLoaded('department')),
];
}
}
```

Tip given by [@mmartin_joo](https://twitter.com/mmartin_joo/status/1473987501501071362)
105 changes: 105 additions & 0 deletions Artisan.md
@@ -0,0 +1,105 @@
## Artisan

⬆️ [Go to main menu](README.md#laravel-tips) ⬅️ [Previous (Mail)](Mail.md) ➡️ [Next (Factories)](Factories.md)

- [Artisan command parameters](#artisan-command-parameters)
- [Maintenance Mode](#maintenance-mode)
- [Artisan command help](#artisan-command-help)
- [Exact Laravel version](#exact-laravel-version)
- [Launch Artisan command from anywhere](#launch-artisan-command-from-anywhere)


### Artisan command parameters

When creating Artisan command, you can ask the input in variety of ways: `$this->confirm()`, `$this->anticipate()`, `$this->choice()`.

```php
// Yes or no?
if ($this->confirm('Do you wish to continue?')) {
//
}

// Open question with auto-complete options
$name = $this->anticipate('What is your name?', ['Taylor', 'Dayle']);

// One of the listed options with default index
$name = $this->choice('What is your name?', ['Taylor', 'Dayle'], $defaultIndex);
```

### Maintenance Mode

If you want to enable maintenance mode on your page, execute the down Artisan command:
```bash
php artisan down
```

Then people would see default 503 status page.

You may also provide flags, in Laravel 8:
- the path the user should be redirected to
- the view that should be prerendered
- secret phrase to bypass maintenance mode
- status code during maintenance mode
- retry page reload every X seconds

```bash
php artisan down --redirect="/" --render="errors::503" --secret="1630542a-246b-4b66-afa1-dd72a4c43515" --status=200 --retry=60
```

Before Laravel 8:
- message that would be shown
- retry page reload every X seconds
- still allow the access to some IP address

```bash
php artisan down --message="Upgrading Database" --retry=60 --allow=127.0.0.1
```

When you've done the maintenance work, just run:
```bash
php artisan up
```

### Artisan command help

To check the options of artisan command, Run artisan commands with `--help` flag. For example, `php artisan make:model --help` and see how many options you have:

```
Options:
-a, --all Generate a migration, seeder, factory, and resource controller for the model
-c, --controller Create a new controller for the model
-f, --factory Create a new factory for the model
--force Create the class even if the model already exists
-m, --migration Create a new migration file for the model
-s, --seed Create a new seeder file for the model
-p, --pivot Indicates if the generated model should be a custom intermediate table model
-r, --resource Indicates if the generated controller should be a resource controller
--api Indicates if the generated controller should be an API controller
-h, --help Display this help message
-q, --quiet Do not output any message
-V, --version Display this application version
--ansi Force ANSI output
--no-ansi Disable ANSI output
-n, --no-interaction Do not ask any interactive question
--env[=ENV] The environment the command should run under
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
```

### Exact Laravel version

Find out exactly what Laravel version you have in your app, by running command
`php artisan --version`

### Launch Artisan command from anywhere

If you have an Artisan command, you can launch it not only from Terminal, but also from anywhere in your code, with parameters. Use Artisan::call() method:

```php
Route::get('/foo', function () {
$exitCode = Artisan::call('email:send', [
'user' => 1, '--queue' => 'default'
]);

//
});
```
82 changes: 82 additions & 0 deletions Auth.md
@@ -0,0 +1,82 @@
## Auth

⬆️ [Go to main menu](README.md#laravel-tips) ⬅️ [Previous (Collections)](Collections.md) ➡️ [Next (Mail)](Mail.md)

- [Check Multiple Permissions at Once](#check-multiple-permissions-at-once)
- [More Events on User Registration](#more-events-on-user-registration)
- [Did you know about Auth::once()?](#did-you-know-about-authonce)
- [Change API Token on users password update](#change-api-token-on-users-password-update)
- [Override Permissions for Super Admin](#override-permissions-for-super-admin)

### Check Multiple Permissions at Once

In addition to `@can` Blade directive, did you know you can check multiple permissions at once with `@canany` directive?

```blade
@canany(['update', 'view', 'delete'], $post)
// The current user can update, view, or delete the post
@elsecanany(['create'], \App\Post::class)
// The current user can create a post
@endcanany
```

### More Events on User Registration

Want to perform some actions after new user registration? Head to `app/Providers/EventServiceProvider.php` and add more Listeners classes, and then in those classes implement `handle()` method with `$event->user` object

```php
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,

// You can add any Listener class here
// With handle() method inside of that class
],
];
```

### Did you know about Auth::once()?

You can login with user only for ONE REQUEST, using method `Auth::once()`.
No sessions or cookies will be utilized, which means this method may be helpful when building a stateless API.

```php
if (Auth::once($credentials)) {
//
}
```

### Change API Token on users password update

It's convenient to change the user's API Token when its password changes.

Model:
```php
public function setPasswordAttribute($value)
{
$this->attributes['password'] = $value;
$this->attributes['api_token'] = Str::random(100);
}
```

### Override Permissions for Super Admin

If you've defined your Gates but want to override all permissions for SUPER ADMIN user, to give that superadmin ALL permissions, you can intercept gates with `Gate::before()` statement, in `AuthServiceProvider.php` file.

```php
// Intercept any Gate and check if it's super admin
Gate::before(function($user, $ability) {
if ($user->is_super_admin == 1) {
return true;
}
});

// Or if you use some permissions package...
Gate::before(function($user, $ability) {
if ($user->hasPermission('root')) {
return true;
}
});
```
105 changes: 105 additions & 0 deletions Collections.md
@@ -0,0 +1,105 @@
## Collections

⬆️ [Go to main menu](README.md#laravel-tips) ⬅️ [Previous (Validation)](Validation.md) ➡️ [Next (Auth)](Auth.md)

- [Don’t Filter by NULL in Collections](#dont-filter-by-null-in-collections)
- [Use groupBy on Collections with Custom Callback Function](#use-groupby-on-collections-with-custom-callback-function)
- [Multiple Collection Methods in a Row](#multiple-collection-methods-in-a-row)
- [Calculate Sum with Pagination](#calculate-sum-with-pagination)
- [Serial no. in foreach loop with pagination](#serial-no-in-foreach-loop-with-pagination)
- [Higher order collection methods](#higher-order-collection-methods)
-

### Don’t Filter by NULL in Collections

You can filter by NULL in Eloquent, but if you're filtering the **collection** further - filter by empty string, there's no "null" in that field anymore.

```php
// This works
$messages = Message::where('read_at is null')->get();

// Won’t work - will return 0 messages
$messages = Message::all();
$unread_messages = $messages->where('read_at is null')->count();

// Will work
$unread_messages = $messages->where('read_at', '')->count();
```

### Use groupBy on Collections with Custom Callback Function

If you want to group result by some condition which isn’t a direct column in your database, you can do that by providing a closure function.

For example, if you want to group users by day of registration, here’s the code:
```php
$users = User::all()->groupBy(function($item) {
return $item->created_at->format('Y-m-d');
});
```

⚠️ Notice: it is done on a `Collection` class, so performed **AFTER** the results are fetched from the database.

### Multiple Collection Methods in a Row

If you query all results with `->all()` or `->get()`, you may then perform various Collection operations on the same result, it won’t query database every time.
```php
$users = User::all();
echo 'Max ID: ' . $users->max('id');
echo 'Average age: ' . $users->avg('age');
echo 'Total budget: ' . $users->sum('budget');
```

### Calculate Sum with Pagination

How to calculate the sum of all records when you have only the PAGINATED collection? Do the calculation BEFORE the pagination, but from the same query.


```php
// How to get sum of post_views with pagination?
$posts = Post::paginate(10);
// This will be only for page 1, not ALL posts
$sum = $posts->sum('post_views');

// Do this with Query Builder
$query = Post::query();
// Calculate sum
$sum = $query->sum('post_views');
// And then do the pagination from the same query
$posts = $query->paginate(10);
```

### Serial no in foreach loop with pagination
We can use foreach collection items index as serial no (SL) in pagination.

```php
...
<th>Serial</th>
...
@foreach ($products as $product)
<tr>
<td>{{ $loop->index + $product->firstItem() }}</td>
...
@endforeach
```
it will solve the issue of next pages(?page=2&...) index count from continue.

### Higher order collection methods

Collections have higher order methods, this are methods that can be chained , like `groupBy()` , `map()` ... Giving you a fluid syntax. This example calculates the
price per group of products on an offer.

```php
$offer = [
'name' => 'offer1',
'lines' => [
['group' => 1, 'price' => 10],
['group' => 1, 'price' => 20],
['group' => 2, 'price' => 30],
['group' => 2, 'price' => 40],
['group' => 3, 'price' => 50],
['group' => 3, 'price' => 60]
]
];

$totalPerGroup = collect($offer->lines)->groupBy('group')->map(fn($group) => $group->sum('price'));
```

0 comments on commit 8e53156

Please sign in to comment.