Ad

Laravel Is It Correct To Put Business Logic In Model?

- 1 answer

I am new to Laravel and I do not quite understand where to put my business logic. At this point, I put the business logic in a model or service (depending on size), so my controllers can be as thin as possible. Is my approach correct? I saw several examples of models in which there were only relationships, but in this case where to store business logic?

Here's examples of my model and service Model

class Test extends Model
{
    protected $fillable = ['name','description','category_id','difficulty','max_time','questions','max_points'];

   public function questions() // questions with answers related to chosen test
   {
       return $this->hasMany(Question::class)->inRandomOrder();
   }

    public function testCategories() // full info about every available category
    {
        return $this->with('category')
            ->select('category_id',
                DB::raw('min(difficulty) as minDifficulty'),
                DB::raw('max(difficulty) as maxDifficulty'),
                DB::raw('count(id) as total'))
            ->groupBy('category_id')
            ->get();
    }
    public function category()  // info about chosen tests category
    {
        return $this->belongsTo(Category::class,'category_id','id');
    }

    public function categoryTests($id,$testName = null)  // list of tests in chosen category
    {
        if(is_null($testName)) {
            return $this->with('category')
                ->where('category_id', $id)
                ->get();
        } else {
            return $this->with('category')
                ->where('name','like','%' . $testName . '%')
                ->get()
                ->sortBy('category_id');
        }
    }

    public function getTestData($id)  // get questions with answers + category
    {
        return $this->with(['questions.answers','category'])->where('id',$id)->get();
    }

    public function users()   // pivot table
    {
        $this->belongsToMany(User::class,'user_test');
    }



// Admin Section

    public function getAdminTestData($data)
    {
        return $this->with(['questionsCount','category'])
            ->where('id',($id ?? '!='),($id ?? 'null'))
            ->orderBy($data['orderBy'] ?? 'id',$data['param'] ?? 'asc')
            ->paginate(15);
    }


    public function questionsCount()
    {
        return $this->hasMany(Question::class)
            ->select('id','test_id',DB::raw('count(test_id) as total'))
            ->groupBy('test_id');

    }

    public function saveTest($data)
    {
       return $this->create([
            'name' => $data['testName'],
            'description' => $data['testDescription'],
            'category_id'=> $data['testCategory'],
            'difficulty' => $data['testDifficulty'],
            'max_time' => $data['maxTime'],
            'questions' => $data['questionsCount'],
            'max_points' => $data['max_points']
        ]);
    }

    public function getTestInfo($id)
    {
       return $this->with('getQuestionsInfo')->where('id',$id)->first();
    }

    public function updateTest($id,$data)
    {
        $this->find($id)->update([
            'name' => $data['name'],
            'category_id' => $data['category_id'],
            'description' => $data['description'],
            'max_time' => $data['max_time'],
            'difficulty' => $data['difficulty'],
        ]);
    }

    public function getQuestionsInfo()
    {
        return $this->hasMany(Question::class)->with(['answersCount']);
    }
}

Service

  public function handleTestCreate(Request $request) : void
    {
        $data =  $request->json()->all();
        $id = $this->model->saveTest($data)->id;
        foreach ($data['questions'] as $question) {
            $questionId = Question::create([
                'question_body' => $question['name'],
                'test_id' => $id,
                'points' => $question['points']
            ])->id;
            foreach ($question['answers'] as $answer) {
                Answer::create([
                    'answer_body' => $answer['name'],
                    'question_id' => $questionId,
                    'is_correct' => $answer['correct']
                ]);
            }
        }
    }
Ad

Answer

You approach is quite opposite of what is recommended. Controllers should handle the interaction between your view and model. A model should only have details related to the model such as relationships and the same applies to the views.

Intro to MVC framework: https://www.tutorialspoint.com/mvc_framework/mvc_framework_introduction.htm

Personally, I would have 3 controllers for Questions, Categories and Admin as well as the Model like so:

Test.php

class Test extends Model
{
    protected $fillable = ['name','description','category_id','difficulty','max_time','questions','max_points'];

    public function category()  // info about chosen tests category
    {
        return $this->belongsTo(Category::class,'category_id','id');
    }

    public function users()   // pivot table
    {
        $this->belongsToMany(User::class,'user_test');
    }

    public function getQuestionsInfo()
    {
        return $this->hasMany(Question::class)->with(['answersCount']);
    }
}

QuestionController.php

use App\Test; // Import Model in Controller

namespace App\Http\Controllers;

class QuestionsController extends Controller
{
    public function questions() // questions with answers related to chosen test
    {
        return $this->hasMany(Question::class)->inRandomOrder();
    }

    public function getTestData($id)  // get questions with answers + category
    {
        return Test::with(['questions.answers','category'])->where('id',$id)->get();
    }
}

CategoriesController.php

<?php

namespace App\Http\Controllers;

class CategoriesController extends Controller
{
    public function testCategories() // full info about every available category    {
        return $this->with('category')
            ->select('category_id',
                DB::raw('min(difficulty) as minDifficulty'),
                DB::raw('max(difficulty) as maxDifficulty'),
                DB::raw('count(id) as total'))
            ->groupBy('category_id')
            ->get();
    }

    public function categoryTests($id,$testName = null)  // list of tests in chosen category
    {
        if(is_null($testName)) {
            return $this->with('category')
                ->where('category_id', $id)
                ->get();
        } else {
            return $this->with('category')
                ->where('name','like','%' . $testName . '%')
                ->get()
                ->sortBy('category_id');
        }
    }

}

Admin Controller

<?php

use App\Test; // Import Model in Controller

namespace App\Http\Controllers;

class AdminController extends Controller
{
    public function getAdminTestData($data)
    {
        return Test::with(['questionsCount','category'])
            ->where('id',($id ?? '!='),($id ?? 'null'))
            ->orderBy($data['orderBy'] ?? 'id',$data['param'] ?? 'asc')
            ->paginate(15);
    }


    public function questionsCount()
    {
        return Test::hasMany(Question::class)
            ->select('id','test_id',DB::raw('count(test_id) as total'))
            ->groupBy('test_id');
    }

    public function saveTest($data)
    {
       return Test::create([
            'name' => $data['testName'],
            'description' => $data['testDescription'],
            'category_id'=> $data['testCategory'],
            'difficulty' => $data['testDifficulty'],
            'max_time' => $data['maxTime'],
            'questions' => $data['questionsCount'],
            'max_points' => $data['max_points']
        ]);
    }

    public function getTestInfo($id)
    {
       return Test::with('getQuestionsInfo')->where('id',$id)->first();
    }

    public function updateTest($id,$data)
    {
        Test::find($id)->update([
            'name' => $data['name'],
            'category_id' => $data['category_id'],
            'description' => $data['description'],
            'max_time' => $data['max_time'],
            'difficulty' => $data['difficulty'],
        ]);
    }
}

Code is not guaranteed as there might be syntax or logical error but the structure is what I'm trying to get across.

Ad
source: stackoverflow.com
Ad