nolros's avatar
Level 23

Best Practice for collection parse to JS?

@JarekTkaczyk @bashy @pmall @rodrigo.pedra or anyone else that has any advice / best practices

Sorry to bug you guys, but if I don’t ask I’ll never learn.

In the case of my code below where I’m retrieving a model with a number of relations. I then parse it to extract a sub set of data that I then return to a view. I use Angular JS as my JS framework and use it for DOM manipulation, filter, etc. - not the JS framework matters that much.

My questions are:

  1. Uuuid / id – I return the objects uuid for JS click events, ng-repeat (foreach), etc. as the unique index for any JS code that requires an index e.g. identify the object for DOM related event code on or any http JS REST server communication. That said it is obviously out in the open. Is this bad practice? If so is there a better way or am I overthinking it? Example, should I use a generate a temp object uuid, then save the actual uuid to Session and then map that temp value with the objects temp value on every server request. Or at very least MD5 the uuid before returning?
  2. Is there a better way / best practice to parsing out data like in toJavaScriptArray() ? Should I be building query scopes for country, industry, etc. or is a foreach() fine. I know the answer is depends, but looking for best practices. As you can see I’m extracting a subset of data from the core object and its relations.
  3. Maybe the same as question 2, but Is there any other smart eloquent approach to pulling this subset of data. Right now it is one DB query which is great, but per the example requires parsing.
  4. Are there any other best practices I should be aware of with this type of approach?

Note: I removed pagination for brevity sake.

Init method is getAllSpaces()

/**
 * Parse spaces with relations to an array to pass to view
 *
 * @return $this
 */
private function toJavaScriptArray()
{
    if (count($this->spaces))
    {
        foreach($this->spaces as $space)
        {
            if ($space instanceof Space && $space->exists)
            {
                $this->arrayOfSpaces[] = [
                    'uuid'                  => $space->uuid,
                    'name'                  => $space->name,
                    'description'           => $space->description,
                    'organization_name'     => $space->organization->name,
                    'timezone_time'         => $space->timezone->time,
                    'timezone_region'       => $space->timezone->region,
                    'industry_code'         => $space->industry->code,
                    'industry_description'  => $space->industry->description,
                    'country_code'          => $space->country->country_code,
                    'country_citizenship'   => $space->country->citizenship,
                    'likes'                 => count($space->likes),
                    'activity'              => count($space->activities)
                    'followers'             => count($space->activities),
                    'tags'                  => count($space->activities),
                    'timestamp'             => date(Carbon::now()),
                ];
            }
        }
    }

    return $this;
}

/**
 * Load / get all spaces with relations
 *
 * @return $this
 */
private function spaces()
{
    $this->spaces = $this->space->with(['organization', 'timezone', 'country', 'industry', 'likes', 'activities', 'followers', 'tags'])->get();

    return $this;
}

/**
 * Return all workspaces
 *
 * @return View
 */
public function getAllSpaces()
{

    $this->spaces()->toJavaScriptArray();

    if (count($this->arrayOfSpaces))
    {
        JavaScript::put([
            'spaces' => $this->arrayOfSpaces
        ]);
    }

    return view('space.main');
}
0 likes
5 replies
henrique's avatar

You can use something like this:

$spaces = $this->space->with(['organization', 'timezone', 'country', 'industry', 'likes', 'activities', 'followers', 'tags'])->get();
$arrayForJS = $spaces->map(function (Space $space) {
    return array(
        'uuid'                  => $space->uuid,
        'name'                => $space->name,
        // ... etc.
    );
});

Map is a function from Laravel Colletion, since Eloquent's get() method return a Collection, you can use it to parse the array, convert anything you need to int, format dates, etc.

1 like
pmall's avatar

I'm sorry man but I almost never understand nothing to your posts. I think you get things way to much complicated. What is this class ? A service ? Which is supposed to do what ?

Why filling an attribute array then return $this then use the attribute array ? Return a freaking array already.

So much questions @nolros, I don't know where to start :D

nolros's avatar
Level 23

@pmall returning $this is not the issue. The question is simple, is there a better way to parse a collection of Eloquent objects for JS. The code above works, what I'm trying to understand is if there are better approaches i.e. just pass the collection to view OR extract sub / condensed set attributes like I'm doing above.

davorminchorov's avatar

Return it as json, pick it up with javascript and show it to the screen?

pmall's avatar

@nolros

If I understand correctly you want to select your data, and print the collection to json. Why all this bullshit ? :)

In your controller / repository / whatever, get the data

$spaces = $i_dont_know_what_object->spaces()->with(['organization', 'timezone', 'country', 'industry', 'likes', 'activities', 'followers', 'tags'])->get();

Print the collection in order to have json

{!! $spaces !!} // Spaces printed as json.

Then I see you want more control over the displayed data. Option 1 quick, make a custom collection and a method formatting your spaces as json

class SpacesCollection extends Collection {

    public function getFormattedAsJsonForMyJavascript()
    {
        $data = [];

        foreach($this as $space)
        {
            'uuid'                  => $space->uuid,
            'name'                  => $space->name,
            'description'           => $space->description,
            'organization_name'     => $space->organization->name,
            'timezone_time'         => $space->timezone->time,
            'timezone_region'       => $space->timezone->region,
            'industry_code'         => $space->industry->code,
            'industry_description'  => $space->industry->description,
            'country_code'          => $space->country->country_code,
            'country_citizenship'   => $space->country->citizenship,
            'likes'                 => count($space->likes),
            'activity'              => count($space->activities)
            'followers'             => count($space->activities),
            'tags'                  => count($space->activities),
            'timestamp'             => date(Carbon::now()),
        }

        return json_encode($data);
    }

}

Then :

{!! $spaces->getFormattedAsJsonForMyJavascript() !!}

Option 2 way cooler : use fractal and a create a SpacesTransformer. It is cool because it separate the collection from the way it is presented.

Thats it :) No bullshit with an internal array in a collection or a Javascript static call. Everything is in place.


I noticed in all the code you post one of the main problem is you have many methods changing the internal state of the object (like here filling the spaces array). Your objects should have the less state possible and do actions instead. Make them do actions and return things !

Also you put the code at wrong places. Here you put the spaces formatting in the parent collection instead of in the spaces collection.

Please or to participate in this conversation.