Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

skoobi's avatar
Level 13

Creating a json api and creating multiple charts

Hi. Sorry this is an update from a post I did a little while ago...

I really have no idea how to do this (my brain is cooked), so hoping someone can help me out. I wanted to try something completely out of my depth and now that i've realised its out of my depth, Its frustrating that I cant find the answer...

What I want to do is, create multiple charts in VueJs from a laravel collection and relationship. So...

I have the api route setup which gets a list of answers that has an answers relationship that belongs to that question.

In Vue, im using axios to get the data and saved it into an array. I then use v-for to iterate through the questions so I can display them in separate boxes with the question text in it. This all works perfect.

The issue im facing is now getting the answers and doing something meaningful with them.

What I need to do is group the answers (answers are either, 1,2,3,4) and count how many answered 1, how many answered 2 etc.

Then I need to display this in the chart as an array [23,45,32,2], but for the correct question.

Heres the route and api call:

Route::get('/v1/questions/{sheet_id}', function (Request $request) {
    $questions = Question::where('sheet_id', $request->sheet_id)->with('answers')->get();
    return  response()->json(['questions' => $questions], Response::HTTP_OK);
});

Heres The Vue template so far:

<template>
	<div>
		<div class="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
			<div v-for="item in questions">


                <dl>
                   <dt class="text-sm leading-5 font-medium">
                      <strong class="text-gray-700">{{ item.title }}</strong> - <strong class="text-gray-500">{{ item.question }}</strong>
                  </dt>
                  <dd>
                      <apexchart
                      type="radialBar"
                      height="390"
                      :options="chartOptions"
                      :series="series">
                  </apexchart>
              </dd>
          </dl>


      </div>
  </div>
</div>
</template>

And here's the script::

<template>
    <div>
        <div class="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
            <div v-for="item in questions">

                <div class="flex flex-col justify-between bg-white overflow-hidden shadow rounded-lg">
                    <div class="px-4 py-5 sm:p-6">
                        <div class="flex items-center">
                            <div class="ml-5 w-0 flex-1">

                                <dl>
                                    <dt class="text-sm leading-5 font-medium">
                                        <strong class="text-gray-700">{{ item.title }}</strong> - <strong class="text-gray-500">{{ item.question }}</strong>
                                    </dt>
                                    <dd>
                                        <apexchart
                                            type="radialBar"
                                            height="390"
                                            :options="chartOptions"
                                            :series="series">
                                        </apexchart>
                                </dd>
                            </dl>

                        </div>
                    </div>
                </div>
            </div>

        </div>
    </div>
</div>
</template>

<script>

    import VueApexCharts from 'vue-apexcharts'

    Vue.component('apexchart', VueApexCharts)

    export default {

        props: {
            sheet_id: Number
        },

        data: function() {
            return {
                questions:[],
                answers: [],
                series: [90,34,56,76],

                chartOptions: {
                    chart: {
                        height: 390,
                        type: 'radialBar',
                    },
                    plotOptions: {
                        radialBar: {
                            offsetY: 0,
                            startAngle: 0,
                            endAngle: 270,
                            hollow: {
                                margin: 5,
                                size: '30%',
                                background: 'transparent',
                                image: undefined,
                            },
                            dataLabels: {
                                name: {
                                    show: false,
                                },
                                value: {
                                    show: false,
                                }
                            }
                        }
                    },
                    colors: ['#E53E3E', '#DD6B20', '#3182CE', '#38A169'],
                    labels: ['Strongly Disagree', 'Disagree', 'Agree', 'Strongly Agree' ],
                    legend: {
                        show: true,
                        position: 'bottom'
                    },
                    legend: {
                        show: true,
                        floating: true,
                        fontSize: '12px',
                        position: 'left',
                        offsetY: 10,
                        labels: {
                            useSeriesColors: true,
                        },
                        markers: {
                            size: 0
                        },
                        formatter: function(seriesName, opts) {
                            return seriesName + ":  " + opts.w.globals.series[opts.seriesIndex]
                        },
                        itemMargin: {
                            vertical: 3
                        }
                    },
                    responsive: [{
                        breakpoint: 480,
                        options: {
                            legend: {
                                show: false
                            }
                        }
                    }]
                },

            }
        },

        created() {
            this.getQuestions()
        },

        methods: {

            getQuestions() {
                axios.get('/api/v1/questions/' + this.sheet_id)
                .then((response)=>{
                    this.questions = response.data.questions
                    this.answers = this.questions['answers']
                })
            },

        },

    };

</script>

I cant figure out how to iterate through the answers and display them in the correct chart for the correct question.

Any pointers or help would be great.

Many thanks

0 likes
5 replies
eggplantSword's avatar
Level 9

I did something similar and what I did was format the data in the controller and send it already formatted and just set it to the charts.

Basically what I did was a foreach on the questions, in my case I had a couple different types of questions so I checked for those and then create some counters. If the answer is 1 verify for each question if that is the answer and increment the counter, and do that for each posible answer, the idea here is to make it as dynamic as posible so you don't end up with 10 if-elses. It depends on what answers you are expected ( multiple choice, yes or no, a number range) and after you have counted the answers I return a different collection that has the info for the graph and other info like the question name, question type, etc.

For example I used Vue Charts and this was how the data needed to be formatted, this is for a yes or no question, this is a basic idea on how to do that. You would need to format the data how the charts you're using specify

$countNo = 0;
$countYes = 0;

foreach($answer as $ans) {
    if ($ans -> answer == 'No') {
        $countNo++;
    } else {
        $countYes++;
    }

    $itemData = [
        'labels' => ['Yes', 'No'],
        'datasets' => [
            array(
                'labels' => 'Result',
                'backgroundColor' => $colors,
                'data' => [$countYes, $countNo]
            )
        ]
    ];
}
skoobi's avatar
Level 13

Hi @msslgomez . Thanks for your reply, definitely reassures me that im heading in the right direction now.

After a walk down the beach, it helped clear things a bit, but this is what I've been working on:

Route::get('/v1/questions/{sheet_id}', function (Request $request) {
    $questions = Question::where('sheet_id', $request->sheet_id)->with('answers')->get();
    $question = [];

    foreach ($questions as $items) {
        array_push(
            $question,
            [
                'title' => $items->title,
                'question' =>$items->question,
                'answers' => [
                    $items->answers->where('answer', 1)->count(),
                    $items->answers->where('answer', 2)->count(),
                    $items->answers->where('answer', 3)->count(),
                    $items->answers->where('answer', 4)->count(),
                ]
            ]
        );
    }

    return  response()->json(['question' => $question], Response::HTTP_OK);
});

I need to add the counter ideally now as the ->count didnt work,

Thank you

eggplantSword's avatar

@skoobi I think it's not working because you're setting the data in the foreach, the counters should go in the foreach and set the data outside the foreach

Route::get('/v1/questions/{sheet_id}', function (Request $request) {
    $questions = Question::where('sheet_id', $request->sheet_id)->with('answers')->get();
    $question = [];

    foreach ($questions as $items) {

	//counters in here;
    }

   $question = [
                'title' => $items->title,
                'question' =>$items->question,
                'answers' => [
                    $counter1,
                    $counter2,
                    $counter3,
                    $counter4,
                ]
            ];

    return  response()->json(['question' => $question], Response::HTTP_OK);
});

Something like this

skoobi's avatar
Level 13

Im getting the correct format for the output but for some reason its not incrementing. Its the same result for every answer for every question.

Route::get('/v1/questions/{sheet_id}', function (Request $request) {
    
$collection = Question::where('sheet_id', $request->sheet_id)->with('answers')->get();

    $questions = Question::where('sheet_id', $request->sheet_id)->with('answers')->get();
    $dataset = [];

    foreach ($questions as $q) {
        $one = 0;
        $two = 0;
        $three = 0;
        $four = 0;

        foreach ($q->answers as $answer) {
            if ($answer->answer == 1 && $answer->question_id == 2) {
                $one++;
            } elseif ($answer->answer == 2 && $answer->question_id == $q->id) {
                $two++;
            } elseif ($answer->answer == 3 && $answer->question_id == $q->id) {
                $three++;
            } elseif ($answer->answer == 4 && $answer->question_id == $q->id) {
                $four++;
            }
        }

        array_push(
            $dataset,
            [
                'id' => $q->id,
                'title' => $q->title,
                'question' => $q->question,
                'answers' => [
                    $one,
                    $two,
                    $three,
                    $four,
                ]
            ]
        );
    }
    return  response()->json(['dataset' => $dataset], Response::HTTP_OK);
});

The results::

{
  "dataset": [
    {
      "id": 1,
      "title": "Q1",
      "question": "My question 1.",
      "answers": [
        0,
        29,
        86,
        24
      ]
    },
    {
      "id": 2,
      "title": "Q2",
      "question": "My question 2",
      "answers": [
        1,
        29,
        86,
        24
      ]
    },
{
      "id": 3,
      "title": "Q3",
      "question": "My question 3",
      "answers": [
        1,
        29,
        86,
        24
      ]
    },
}

Unfortunately the answers are wrong/all the same. The answers are correct for the first question but thats it.

Ill try to lookup collections and see if I can do anything with that to filter and map the array.

Sorry here's my revised collection::

Route::get('/v1/questions/{sheet_id}', function (Request $request) {
    $collection = Question::where('sheet_id', $request->sheet_id)->with('answers')->get();

    $dataset = $collection->map(function ($item) {
        return [
            'id' => $item['id'],
            'title' => $item['title'],
            'question' => $item['question'],
            'answers' => [
                12,23,44,22
            ]
        ];
    });

    return  response()->json(['dataset' => $dataset], Response::HTTP_OK);
});

Just need to fill in the answers.

skoobi's avatar
Level 13

Ok... Solved using collections :)

Route::get('/v1/questions/{sheet_id}', function (Request $request) {
    $collection = Question::where('sheet_id', $request->sheet_id)->with('answers')->get();

    $dataset = $collection->map(function ($item) {
        return [
                'title' => $item['title'],
                'question' => $item['question'],
                'answers' => [
                    collect($item['answers']->where('answer', 1))->count(),
                    collect($item['answers']->where('answer', 2))->count(),
                    collect($item['answers']->where('answer', 3))->count(),
                    collect($item['answers']->where('answer', 4))->count(),
                ]
            ];
    })->all();

    return  response()->json(['dataset' => $dataset], Response::HTTP_OK);
});

Turns out the database that I was collecting from, hadn't transferred the data across correctly and as a result had the exact same figures throughout!!!! Gutted isn't the word... I knew I wasn't going mad

Please or to participate in this conversation.