# Shop: detail section
- Clicking on "show tracks" will show a popup with the tracks of the album
- The tracks are not stored in the database, but are retrieved from the MusicBrainz API (opens new window)
where the attribute
mb_id
in our database is the unique key of the album- E.g. the album Rumours by Fleetwood Mac has the mb_id value 081ea37e-db59-4332-8cd2-ad020cb93af6
- https://musicbrainz.org/ws/2/release/081ea37e-db59-4332-8cd2-ad020cb93af6?inc=recordings&fmt=json (opens new window)
- We will extract the tracks (position, title and length) from the JSON response and show them in a table
# Get selected record
- We will use the
wire:click
attribute on the Show Tracks icon to pass theid
of the selected record to theshowTracks
method in the component - The
showTracks
method will retrieve the record from the database and store it in a new public property$selectedRecord
livewire/shop.blade.php
Livewire/Shop.php
result
- Add the
wire:click
attribute to the Show Tracks icon
<div class="w-6 cursor-pointer hover:text-red-900"> <x-phosphor-music-notes-light wire:click="showTracks({{ $record->id }})" class="outline-0" data-tippy-content="Show tracks"/> </div>
Copied!
1
2
3
4
5
6
2
3
4
5
6
- Instead of only the
id
we want to store the complete record in the public property$selectedRecord
- The long way is to query the database for the record with the
id
and store the result in the public property$selectedRecord
- But there is a better way: we can use Laravel's Route Model Binding (opens new window) to get the complete record with only the
id
as a parameter in the methodshowTracks
Livewire/Shop.php
result
- Line 1: type-hint the parameter
$record
with the modelRecord
=> replaceshowTracks($record)
withshowTracks(Record $record)
- Line 4: dump the property
$selectedRecord
as an array to the page (add->toArray()
)
public function showTracks(Record $record) { $this->selectedRecord = $record; dump($this->selectedRecord->toArray()); }
Copied!
1
2
3
4
5
2
3
4
5
# Add a modal to the view
- Now we have the complete record in the public property
$selectedRecord
, we can use it to pop up a modal - Let's start with only the artist, the title of the album and the cover image
livewire/shop.blade.php
result
- Paste the following code just before the last closing
</div>
tag - When the page first loads, the property
$selectedRecord
is empty and to avoid errors, we need to catch this in the model- Line 6: if
$selectedRecord->title
doesn't exist, show the stringTitle
- Line 7: if
$selectedRecord->artist
doesn't exist, show the stringArtist
- Line 10: if
$selectedRecord->cover['url]
doesn't exist, show a dummy cover image - Line 12: if
$selectedRecord
has notracks
, hide the table
(We push thetracks
later in the array, so for now it doesn't exist)
- Line 6: if
{{-- Detail section --}} <div class="fixed z-40 inset-0 p-8 grid h-screen place-items-center backdrop-blur-sm backdrop-grayscale-[.7] bg-slate-100/70"> <div class="bg-white p-4 border border-gray-300 max-w-2xl"> <div class="flex justify-between space-x-4 pb-2 border-b border-gray-300"> <h3 class="font-medium"> {{ $selectedRecord->title ?? 'Title' }}<br/> <span class="font-light">{{ $selectedRecord->artist ?? 'Artist' }}</span> </h3> <img class="w-20 h-20" src="{{ $selectedRecord->cover['url'] ?? asset('storage/covers/no-cover.png') }}" alt=""> </div> @isset($selectedRecord->tracks) <table class="w-full text-left align-top"> <thead> <tr> <th class="px-4 py-2">#</th> <th class="px-4 py-2">Track</th> <th class="px-4 py-2">Length</th> </tr> </thead> <tbody> <tr class="border-t border-gray-100"> <td class="px-4 py-2">........</td> <td class="px-4 py-2">........</td> <td class="px-4 py-2">........</td> </tr> </tbody> </table> @endisset </div> </div>
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# Show and hide the modal with Alpine
- Luckily for us, Alpine and Livewire work together very well (both frameworks are from the same author)
- With
@entangle
we can sync a public property from the Livewire component with an Alpine variable- When a property in Livewire changes, the Alpine variable changes too
- When the Alpine variable changes, the Livewire property changes too
- This is the technique we're going to use to show and hide the modal
Livewire/Shop.php
livewire/shop.blade.php
result
- Line 2: add a public property
$showModal
and set it default tofalse
(We can use the value of this variable with@entangle('showModal')
in Alpine) - Line 14: after loading the selected record, set the property
$showModal
totrue
class Shop extends Component { use WithPagination; // public properties public $perPage = 6; public $loading = 'Please wait...'; public $selectedRecord; public $showModal = false; public function showTracks(Record $record) { $this->selectedRecord = $record; $this->showModal = true; // dump($this->selectedRecord->toArray()); } ... }
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Get all tracks for the selected record
- Get tracks info from MusicBrainz API (opens new window) with the Laravel HTTP client (opens new window)
- Get only the JSON data from the result
Livewire/Shop.php
result
- Line 4: the URL which contains the
mb_id
of the selected record - Line 5:
$response
fetches the URL with the Laravel HTTP client (includeuse Http;
) and convert the result to JSON data
($response
contains all the data from the API, like we can see in the first screenshot of this chapter) - Line 6:
- push a new key
tracks
to the selected record$this->selectedRecord['tracks']
- select only the tracks-array in the response (
$response['media'][0]['tracks']
) and add them to thetracks
key
- push a new key
- Line 7: (temporary) dump and die the selected record, with the tracks, to the page
public function showTracks(Record $record) { $this->selectedRecord = $record; $url = "https://musicbrainz.org/ws/2/release/{$record->mb_id}?inc=recordings&fmt=json"; $response = Http::get($url)->json(); $this->selectedRecord['tracks'] = $response['media'][0]['tracks']; dd($this->selectedRecord->toArray()); $this->showModal = true; }
Copied!
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# Show tracks in the modal
livewire/shop.blade.php
result
- Line 11 - 17: loop over the tracks and show the title and duration (you don't need a
wire:key
here, because it's a static list) - Line 13 - 55: show the
position
, thetitle
and the duration (length
) of each track
@isset($selectedRecord->tracks) <table class="w-full text-left align-top"> <thead> <tr> <th class="px-4 py-2">#</th> <th class="px-4 py-2">Track</th> <th class="px-4 py-2">Length</th> </tr> </thead> <tbody> @foreach($selectedRecord['tracks'] as $track) <tr class="border-t border-gray-100"> <td class="px-4 py-2">{{ $track['position'] }}</td> <td class="px-4 py-2">{{ $track['title'] }}</td> <td class="px-4 py-2">{{ $track['length'] }}</td> </tr> @endforeach </tbody> </table> @endisset
Copied!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
dd
- Don't forget to comment the dump in
Livewire/Shop.php
!
# Reformat the track length with Carbon
- The track length is in milliseconds, so we need to convert it to mm:ss
- You can make a custom function for it that handles the transformation, but we prefer to use the Carbon (opens new window) package for this
- Carbon is an easy to use PHP extension for date/time manipulation and formatting
- Carbon is by default installed in in every Laravel project
- The Carbon method we need is
createFromTimestampMs($milliseconds)->format('i:s')
(opens new window)createFromTimestampMs
creates a Carbon object from a timestamp in$milliseconds
format
formats the Carbon object to a string
livewire/shop.blade.php
result
- Line 6: wrap the length of the song in a Carbon object
- IMPORTANT
- Because there is no Blade helper for Carbon, we need to use the full namespace for the class
Carbon\Carbon::createFromTimestampMs($milliseconds)->format('i:s')
- Another option is to use the
use
statement at the top of the file:@php use Carbon\Carbon; @endphp
and then useCarbon::createFromTimestampMs($milliseconds)->format('i:s')
in the view
- Because there is no Blade helper for Carbon, we need to use the full namespace for the class
<tbody> @foreach($selectedRecord['tracks'] as $track) <tr class="border-t border-gray-100"> <td class="px-4 py-2">{{ $track['position'] }}</td> <td class="px-4 py-2">{{ $track['title'] }}</td> <td class="px-4 py-2">{{ \Carbon\Carbon::createFromTimestampMs($track['length'])->format('i:s') }}</td> </tr> @endforeach </tbody>
Copied!
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9