Shop: detail page

  • Open the detail view resources/views/shop/show.blade.php
  • Open the controller app/Http/Controllers/ShopController.php

Basic view

  • Create a basic, static skeleton for the record details


 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


@section('main')
    <h1>Artist - Record</h1>
    <div class="row">
        <div class="col-sm-4 text-center">
            <img class="img-thumbnail" id="cover" src="/assets/vinyl.png" data-src="" alt="">
            <p>
                <a href="#!" class="btn btn-sm  btn-block mt-3">
                    <i class="fas fa-cart-plus mr-3"></i>Add to cart
                </a>
            </p>
            <p class="text-left">Genre: <br>
                Stock: <br>
                Price:</p>
        </div>
        <div class="col-sm-8">
            <table class="table table-sm">
                <thead>
                <tr>
                    <th scope="col">#</th>
                    <th scope="col">Track</th>
                    <th scope="col">Length</th>
                </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </div>
    </div>
@endsection
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

Update ShopController

  • You only retrieve one record from the database
  • Use the number at the end of the URL to identify the record to be shown

Get one record

  • Select one record based on the $id at the end of the URL using the method findOrFail()


 
 
 
 
 


public function show($id)
{
    $record = Record::with('genre')->findOrFail($id);
    //dd($record);
    $result = compact('record');
    Json::dump($result);
    return view('shop.show', $result);  // Pass $result to the view
}
1
2
3
4
5
6
7
8

One record

REMARK

If the database finds no result for a given id (e.g. http://localhost:3000/shop/555?json), the method findOrFail() automatically results in a 404 | Not Found error (in contrast to the method find() which would result in a null object)

Transform the record

  • The reformatting of $record is (a bit) different than the transform of $genres in the previous section!
  • $genres from the previous section was a collection with an array of Genre objects
    • See the output below of dd($genres) (before the transform)
    • You can loop over each genre with transform()

Collection of genres

  • $record is NOT a collection, but one object of the class Record
    • See the output below of dd($record)
    • You can't loop and therefore can't use transform()

One record object

  • Transform the properties of the record and remove all properties you don't use inside the view



 

 


 
 

 

 

 




$record = Record::with('genre')->findOrFail($id);
// dd($record);
// Real path to cover image
$record->cover = $record->cover ?? "https://coverartarchive.org/release/$record->title_mbid/front-250.jpg";
// Combine artist + title
$record->title = $record->artist . ' - ' . $record->title;
// Links to MusicBrainz API (used by jQuery)
// https://wiki.musicbrainz.org/Development/JSON_Web_Service
$record->artistUrl = 'https://musicbrainz.org/ws/2/artist/' . $record->artist_mbid . '?inc=url-rels&fmt=json';
$record->recordUrl = 'https://musicbrainz.org/ws/2/release/' . $record->title_mbid . '?inc=recordings+url-rels&fmt=json';
// If stock > 0: button is green, otherwise the button is red
$record->btnClass = $record->stock > 0 ? 'btn-outline-success' : 'btn-outline-danger';
// You can't overwrite the attribute genre (object) with a string, so we make a new attribute
$record->genreName = $record->genre->name;
// Remove attributes you don't need for the view
unset($record->genre_id, $record->artist, $record->created_at, $record->updated_at, $record->artist_mbid, $record->title_mbid, $record->genre);
$result = compact('record');
Json::dump($result);
return view('shop.show', $result);  // Pass $result to the view
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

Transform $record

Add data to the view

  • Update the view with the data from the query
    • Add title to the 'title' section (without curly brackets!), to the h1 tag and inside the alt attribute of the image
    • Add cover inside the data attribute data-src of the image
    • Add btnClass as an extra class to the button
    • Add genreName, stock and price to the page
    • If stock is 0, add the attribute disabled to the button
 


 


 
 

 
 



 
 
 

















@section('title', $record->title)

@section('main')
    <h1>{{ $record->title }}</h1>
    <div class="row">
        <div class="col-sm-4 text-center">
            <img class="img-thumbnail" id="cover" src="/assets/vinyl.png" data-src="{{ $record->cover }}" 
             alt="{{ $record->title }}">
            <p>
                <a href="#!" class="btn {{ $record->btnClass }} btn-sm btn-block mt-3 
                 {{ $record->stock == 0 ? 'disabled' : '' }}">
                    <i class="fas fa-cart-plus mr-3"></i>Add to cart
                </a>
            </p>
            <p class="text-left">Genre: {{ $record->genreName }}<br>
                Stock: {{ $record->stock }}<br>
                Price:{{ number_format($record->price,2) }}</p>
        </div>
        <div class="col-sm-8">
            <table class="table table-sm">
                <thead>
                <tr>
                    <th scope="col">#</th>
                    <th scope="col">Track</th>
                    <th scope="col">Length</th>
                </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </div>
    </div>
@endsection
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
32
33

Add data to the view

Add dynamic content with jQuery

  • Replace the dummy cover with the real one (exactly the same as in previous section) and get all the tracks on the album from the MusicBrainz api

Show the real cover

  • Fill the script_after gap in the detail page resources/views/shop/show.blade.php




 





@section('script_after')
    <script>
        $(function () {
            // Replace vinyl.png with real cover
            $('#cover').attr('src', $('#cover').data('src'));
            // Get tracks from MusicBrainz API
        });
    </script>
@endsection
1
2
3
4
5
6
7
8
9

Get all tracks

  • Show the JSON representation of an album, e.g: http://localhost:3000/shop/5?json
  • Click on the recordUrl value and inspect the JSON response
    • You find the information about a track inside media (an array of one item) and tracks (an array of x JSON objects)
    • To get all tracks, loop over media[0].tracks
    • To get the title of the first track: media[0].tracks[0].title

Find tracks on MusicBrains API

  • Get all tracks and add them inside the tbody tag of the table
    • Use the getJSON() method that loads JSON-encoded data from the server using a GET HTTP request
    • Open the browser console to see all the data (or errors)
// Get tracks from MusicBrainz API
$.getJSON('{{ $record->recordUrl }}')
    .done(function (data) {
        console.log(data);
        // loop over each track
        $.each(data.media[0].tracks, function (key, value) {
            // Construct a table row
            let row = `<tr>
                <td>${value.position}</td>
                <td>${value.title}</td>
                <td>${value.length}</td>
            </tr>`;
            // Append the row to the tbody tag
            $('tbody').append(row);
        });
    })
    .fail(function (error) {
        console.log("error", error);
    })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

Add tracks to table

Update the track length

  • The track length is in ms but we want to format it as mm:ss
  • Because we have to format all track lengths this way, it's best practice to do this with a function
    • Make a function to_mm_ss() and pass the track length to this function
    • At the moment, you don't see any difference in the view





 




 
 
 

$(function () {
    ...
    let row = `<tr>
        <td>${value.position}</td>
        <td>${value.title}</td>
        <td>${to_mm_ss(value.length)}</td>
    </tr>`;
    ...
});

function to_mm_ss(duration) {
    return duration;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
  • Convert the duration inside the function to a mm:ss format
function to_mm_ss(duration) {
    let seconds = parseInt((duration / 1000) % 60);
    let minutes = parseInt(duration / (1000 * 60));
    minutes = (minutes < 10) ? '0' + minutes : minutes;
    seconds = (seconds < 10) ? '0' + seconds : seconds;
    duration = minutes + ':' + seconds;
    return duration;
}
1
2
3
4
5
6
7
8

Convert track length to mm:ss

Move script to global vinylShop.js

  • If you need this script on other pages within your site, you should move it to the common script vinylshop.js in our site
    • Open resources/js/vinylShop.js and move the function to_mm_ss(duration) to this file (don't forget to delete this function from show.blade.php)
    • Add export in front of the function
 








export function to_mm_ss(duration) {
    let seconds = parseInt((duration / 1000) % 60);
    let minutes = parseInt((duration / (1000 * 60)) % 60);
    minutes = (minutes < 10) ? '0' + minutes : minutes;
    seconds = (seconds < 10) ? '0' + seconds : seconds;
    duration = minutes + ':' + seconds;
    return duration;
}
1
2
3
4
5
6
7
8
  • Update the reference inside the Blade template from to_mm_ss(value.length) to vinylShop.to_mm_ss(value.length)



 


let row = `<tr>
    <td>${value.position}</td>
    <td>${value.title}</td>
    <td>${vinylShop.to_mm_ss(value.length)}</td>
</tr>`;
1
2
3
4
5
  • If the script is working, you don't see any difference on the page

EXERCISE: Track lengths

  • Check the detail page for the record 'Cock Sparrer - Forever' (with id = 31)

Record with zero track lengths

  • Notice that the track lengths are 00:00
  • Dig into the MusicBrainz record/release details (that we retrieve in JSON-format), and resolve this issue
  • Check whether your solution also works for the other records!

Commit "Opdracht 5: <= Shop: detail page"

  • Execute the following commands in a terminal window:
git add .
git commit -m "Opdracht 5: <= Shop: detail page"
git push
1
2
3
Last Updated: 10/23/2019, 9:11:32 AM