Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/reinink/wants

Source code for my example app used in my Laracon Online 2020 talk
https://github.com/reinink/wants

Last synced: about 2 months ago
JSON representation

Source code for my example app used in my Laracon Online 2020 talk

Awesome Lists containing this project

README

        

# Wants

Source code for my example app used in my [Laracon Online](https://laracon.net) 2020 talk.

![](https://raw.githubusercontent.com/reinink/wants/master/screenshot.png)

## Database Schema

- `users`
- `categories`
- `wants`
- `comments`
- `votes`

## Install the Laravel Debugbar

- `composer require barryvdh/laravel-debugbar --dev`
- Set `APP_DEBUG=true` in `.env`
- `php artisan vendor:publish` (9)
- Enable `models` in `config/debugbar.php`
- *Goals*
1. Minimize database queries.
2. Minimize hydrated models.
3. Minimize memory usage.

## Requirement 1: Show status totals on dashboard

Update `views/wants.blade.php`:

```html







Requested


10







Planned


10







Completed


10



```

Update `Controllers/WantsController.php`

```php
$wants = Want::all();
$statuses = (object) [];
$statuses->requested = $wants->where('status', 'Requested')->count();
$statuses->planned = $wants->where('status', 'Planned')->count();
$statuses->completed = $wants->where('status', 'Completed')->count();

return View::make('wants', [
'statuses' => $statuses,
'wants' => $wants,
]);
```

Update `views/wants.blade.php`:

```php
{{ $statuses->requested }}
{{ $statuses->planned }}
{{ $statuses->completed }}
```

Update `Controllers/WantsController.php`

```php
$statuses = (object) [];
$statuses->requested = Want::where('status', 'Requested')->count();
$statuses->planned = Want::where('status', 'Planned')->count();
$statuses->completed = Want::where('status', 'Completed')->count();
```

Update `Controllers/WantsController.php`

```php
$statuses = Want::getQuery()
->selectRaw("count(case when status = 'Requested' then 1 end) as requested")
->selectRaw("count(case when status = 'Planned' then 1 end) as planned")
->selectRaw("count(case when status = 'Completed' then 1 end) as completed")
->first();
```

## Requirement 2: Add comments links and author label

Update `app/Comment.php`

```php
public function url()
{
return $this->want->url().'#comment-'.$this->id;
}
```

Update `views/want.blade.php`:

```html

{{ $comment->created_at->format('M j, Y \a\t g:i a') }}

```

Update `Controllers/WantsController.php`

```php
$want->load(['comments' => function ($query) {
$query->with('user', 'want');
}]);
```

*Notes:*
- Still making two unnecessary database queries (`Want` and `Want::category()`).
- Let's amplify this problem.

Update `views/wants.blade.php`

```html
@if ($comment->isAuthor())



Author


@endif
```

Update `app/Comment.php`

```php
public function isAuthor()
{
return $this->want
->comments
->sortBy('created_at')
->first()
->user
->is($this->user);
}
```

*What we have:*
- want
- category `(to show at the top)`
- comments `(to list the comments)`
- user `(to show the comment user)`
- **want**
- category `(for the link)`
- comments `(to determine first comment)`
- user `(to get the author)`
*What we want:*
- want
- category
- comments
- user

Update `Controllers/WantsController.php`

```php
$want->load('comments.user');
$want->comments->each->setRelation('want', $want);
```

*Notes:*
- What we're doing is optimizing in the perimeter of our app.
- Let's look at one last example.

Update `Controllers/WantsController.php`

```php
$want->load('comments.user:id,name,photo');
```

## Requirement 3: Add last comment to dashboard

Update `views/wants.blade.php`

```html


Last comment
@if (Request::input('sort') === 'last_comment')

@endif

```

```html







Jonathan Reinink


Feb 22, 2020 at 3:52 pm


```

Update `app/Want.php`

```php
public function getLastCommentAttribute()
{
return $this->comments->sortByDesc('created_at')->first();
}
```

Update `views/wants.blade.php`

```php
{{ $want->lastComment->user->photo }}
{{ $want->lastComment->user->name }}
{{ $want->lastComment->created_at->format('M j, Y \a\t g:i a') }}
```

Update `Controllers/WantsController.php`

```php
$wants = Want::query()
->with('category', 'comments.user')
```

*Notes:*
- We're now loading a TON of data.
- We're also loading too many users.
- And this problem becomes much worse if we show more than 15 results per page.
- Try `->paginate(100)`

Update `app/Want.php`

```php
public function lastComment()
{
return $this->belongsTo(Comment::class);
}
```

*Notes:*
- But `wants.last_comment_id` does not exist.
- How can we make this relationship work?

```php
public function scopeWithLastCommentId($query)
{
$query->addSelect(['last_comment_id' => Comment::select('id')
->whereColumn('comments.want_id', 'wants.id')
->latest()
->take(1),
]);
}
```

Update `Controllers/WantsController.php`

```php
$wants = Want::query()
->with('category', 'lastComment.user')
```

Update `Controllers/WantsController.php`

```php
$wants = Want::query()
->withLastCommentId()
```

Update `app/Want.php`

```php
protected static function boot()
{
parent::boot();

static::addGlobalScope('with_last_comment_id', function ($query) {
$query->withLastCommentId();
});
}
```

Update `Controllers/WantsController.php`

```php
$wants = Want::query()
->with('category', 'lastComment.user')
```

## Requirement 4: Add column sorting to dashboard

Update `Controllers/WantsController.php`

```php
->when(Request::input('sort'), function ($query, $sort) {
switch ($sort) {
case 'category': return $query->orderByCategory();
case 'last_comment': return $query->orderByLastCommentDate();
case 'status': return $query->orderByStatus();
case 'activity': return $query->orderByActivity();
}
})
```

Update `app/Want.php`

```php
public function scopeOrderByCategory($query)
{
}

public function scopeOrderByLastCommentDate($query)
{
}

public function scopeOrderByStatus($query)
{
}

public function scopeOrderByActivity($query)
{
}
```

Update `app/Want.php`

```php
public function scopeOrderByCategory($query)
{
$query->orderBy(
Category::select('name')
->whereColumn('categories.id', 'wants.category_id')
);
}
```

Update `app/Want.php`

```php
public function scopeOrderByLastCommentDate($query)
{
$query->orderByDesc(
Comment::select('created_at')
->whereColumn('comments.want_id', 'wants.id')
->latest()
->take(1)
);
}
```

Update `app/Want.php`

```php
public function scopeOrderByStatus($query)
{
$query->orderByDesc('status');
}
```

Update `app/Want.php`

```php
public function scopeOrderByStatus($query)
{
$query->orderByRaw("
case
when status = 'Requested' then 1
when status = 'Planned' then 2
when status = 'Completed' then 3
end
");
}
```

Update `app/Want.php`

```php
public function scopeOrderByActivity($query)
{
$votes = Vote::selectRaw('count(*)')
->whereColumn('votes.want_id', 'wants.id')
->toSql();

$comments = Comment::selectRaw('count(*)')
->whereColumn('comments.want_id', 'wants.id')
->toSql();

$query->orderByRaw("($votes) + (($comments) * 2) desc");
}
```

*Notes:*
- Test it using by adding it as a column:

```php
$query->selectRaw("($votes) + (($comments) * 2) as activity");
```