Laravel - How to make students not to score beyond the max mark - laravel

I am using Laravel-5.8 for a web application project. In the project I have these tables:
class SubjectCategory extends Model
{
protected $table = 'subject_categories';
protected $fillable = [
'name',
'parent_id',
'max_mark',
];
public function children()
{
return $this->hasMany('App\Models\SubjectCategory', 'parent_id');
}
public function exams()
{
return $this->hasMany('App\Models\Exam');
}
}
class Exam extends Model
{
protected $table = 'exams';
protected $fillable = [
'subject_category_id',
'student_id',
'student_mark',
'subject_name',
];
public function subjectcategory()
{
return $this->belongsTo('App\Models\SubjectCategory','subject_category_id');
}
}
SubjectCategory is an hierarchical table. Only the parent have the max_mark
Here is the Controller
public function create()
{
$categories = SubjectCategory::with('children')->whereNull('parent_id')->get();
return view('exams.create')
->with('categories', $categories);
}
public function store(StoreExamRequest $request)
{
$exam = new Exam();
$exam->stubject_category_id = $request->stubject_category_id;
$exam->student_id = $student_id;
$exam->student_mark = $request->student_mark;
$exam->save();
return redirect()->route('exams.index');
}
view blade
<div class="row">
<div class="col-md-12">
<!-- general form elements -->
<div class="card card-secondary">
<form method="POST" action="{{route('exams.store')}}">
#csrf
<div class="card-body">
<div class="form-body">
<div class="row">
<div class="col-12 col-sm-6">
<div class="form-group">
<label class="control-label"> Subject Category:<span style="color:red;">*</span></label>
<select id="subject_category" class="form-control" name="subject_category_id">
<option value="">Select Subject Category</option>
#foreach ($categories as $category)
<option disabled="disabled" value="{{ $category->id }}" {{ $category->id == old('category_id') ? 'selected' : '' }}>{{ $category->name }}</option>
#if ($category->children)
#foreach ($category->children as $child)
<option value="{{ $child->id }}" {{ $child->id == old('category_id') ? 'selected' : '' }}> {{ $child->name }}</option>
#endforeach
#endif
#endforeach
</select>
</div>
</div>
<div class="col-12 col-sm-6">
<div class="form-group">
<label class="control-label"> Subject Name:<span style="color:red;">*</span></label>
<input type="text" name="subject_name" placeholder="Enter Subject Name here" class="form-control">
</div>
</div>
<div class="col-12 col-sm-4">
<div class="form-group">
<label class="control-label"> Mark Obtained:</label>
<input type="number" name="student_mark" placeholder="Enter Mark Obtained here" class="form-control">
</div>
</div>
</div>
</div>
</div>
<!-- /.card-body -->
<div class="card-footer">
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
</div>
<!-- /.card -->
</div>
<!--/.col (left) -->
</div>
Subjects are categorized. From SubjectCategory, the parent subject have sub subjects as children. Only the Parent Subject Category have the Maximum Obtainable Score (Max Mark).
From the Exam (subject_category_id) dropdownlist contains all the children fields from subject_categories. What I want to achieve is this:
When Subject Category is selected from the dropdownlist, the system goes to the exams table. It displays total student_mark based on student_id and subject_category_id.
In each subject, the student accumulated mark cannot be more than the max_mark in the parent subject_category.
When the user tries to enter data into student_mark text field, the application adds the value in the text field to the student aggregate. If the result is more than the max_mark in subject_categories (SubjectCategory) based on the parent max_mark, then an error message is displayed.
How do I achieve this?
Thank you.

This is achievable in a couple of different ways; you could use validation to prevent someone entering a value over the max mark, or if they do enter a value over the max mark then you just silently reduce it back to the max mark.
I would suggest you take the validation approach and to do this you would need to use a custom validation rule.
You can read up on the documentation for how to create one (https://laravel.com/docs/5.8/validation#custom-validation-rules) but the implementation might look a little something like this:
// Inside a form request (or you could use inline validation in your controller
'student_mark' => [new NotOverMaxMark($this->input('subject_category_id'))]
Then for the rule itself might look a little something like this:
<?php
namespace App\Rules;
use App\SubjectCategory;
use Illuminate\Contracts\Validation\Rule;
class NotOverMaxMark implements Rule
{
private $subjectCategory;
public function __construct($subjectCategoryId)
{
$this->subjectCategory = SubjectCategory::findOrFail($subjectCategoryId);
}
public function passes($attribute, $value)
{
return $value <= $this->subjectCategory->max_mark;
}
public function message()
{
return 'The :attribute must not be higher than the max mark.';
}
}
The above is an example of how you might do this, you may need to tweak the imports or implementation to your needs.

Related

my livewire app is returning 404 not found when I selected a dropdown item

I am designing a laravel app, which I used module and livewire. I have a dropdown item which when I selected an item its returned an error 404 NOT FOUND
Here is my livewire component.
I am thinking may the wire:model="selectedSession" and wire:model="selectedTerm" is having problem
please i need your help
<?php
namespace Modules\Student\Http\Livewire;
use Livewire\Component;
use Modules\Student\Entities\Result;
use Modules\Student\Entities\Term;
use Modules\Student\Entities\Section;
class ResultDependance extends Component
{
public $selectedSession = null;
public $selectedTerm = null;
public $results;
public function mount($id)
{
$this->results = Result::with('student', 'section', 'term', 'subject')->where('student_id', $id)->get();
}
public function render()
{
return view('student::livewire.pages.resultdependance',
['terms' => Term::all()],
['sessions' => Section::all()]
);
}
}
my blade.php codes
<div class="section">
<div class="card">
<div class="card-body">
<div class="card-title">
<h4><strong>Result</strong></h4>
<div class="form">
<form action="" method="post">
{{csrf_field()}}
<div class="form-group">
<select name="session" id="" class="form-control form-select" wire:model="selectedSession">
<option selected>Select Session</option>
#foreach($sessions as $session)
<option value="{{$session->id}}">{{$session->section_title}}</option>
#endforeach
</select>
</div>
<br>
<div class="form-group">
<select name="term" id="" class="form-control form-select" wire:model="selectedTerm">
<option selected>Select Term</option>
#foreach($terms as $term)
<option value="{{$term->id}}">{{$term->term}}</option>
#endforeach
</select>
</div>
<input type="button" value="test" wire::loading.attr='disabled'>
<h1 wire:loading>please wait</h1>
</form>
<br>
</div>
</div>
</div>
</div>
</div>
I am designing a laravel app, which I used module and livewire. I have a dropdown item which when I selected an item its returned an error 404 NOT FOUND
Here is my livewire component.
I am thinking may the wire:model="selectedSession" and wire:model="selectedTerm" is having problem
please i need your help
It's missing the logic to filter the results based on the selected session and term.
you can do it by using livewire hooks or by adding this
public function filterResults()
{
$this->results = Result::with('student', 'section', 'term', 'subject')->where('student_id', $id)->where('section_id', $this->selectedSession)->where('term_id', $this->selectedTerm)->get();
}
and in your selectedSession
<select name="session" id="" class="form-control form-select" wire:model.lazy="selectedSession" wire:change="filterResults">
Now when the user changes the selected session or term, the component will filter the results and update the view accordingly.

Laravel - Validate textinput value with database value

I am using Laravel-5.8 for a web application. In the project I want the users to set goals using these two tables:
class GoalType extends Model
{
protected $table = 'goal_types';
protected $fillable = [
'name',
'parent_id',
'is_current',
'max_score',
];
public function children()
{
return $this->hasMany('App\Models\GoalType', 'parent_id');
}
public function goals()
{
return $this->hasMany('App\Models\Goal');
}
}
class Goal extends Model
{
protected $table = 'appraisal_goals';
protected $fillable = [
'goal_type_id',
'employee_id',
'weighted_score',
'goal_description',
'goal_title',
];
public function goaltype()
{
return $this->belongsTo('App\Models\GoalType','goal_type_id');
}
}
As shown in the diagram below, GoalType is an hierarchical table. Only the parent have the max_score:
Controller
public function create()
{
$userCompany = Auth::user()->company_id;
$categories = GoalType::with('children')->where('company_id', $userCompany)->whereNull('parent_id')->get();
return view('goals.create')
->with('categories', $categories);
}
public function store(StoreGoalRequest $request)
{
$employeeId = Auth::user()->employee_id;
$goal = new Goal();
$goal->goal_type_id = $request->goal_type_id;
$goal->employee_id = $employeeId;
$goal->weighted_score = $request->weighted_score;
$goal->save();
Session::flash('success', 'Goal is created successfully');
return redirect()->route('goals.index');
}
create.blade
<div class="row">
<div class="col-md-12">
<!-- general form elements -->
<div class="card card-secondary">
<!-- /.card-header -->
<!-- form start -->
<form method="POST" action="{{route('goals.store')}}">
#csrf
<div class="card-body">
<div class="form-body">
<div class="row">
<div class="col-12 col-sm-6">
<div class="form-group">
<label class="control-label"> Goal Type:<span style="color:red;">*</span></label>
<select id="goal_type" class="form-control" name="goal_type_id">
<option value="">Select Goal Type</option>
#foreach ($categories as $category)
#unless($category->name === 'Job Fundamentals')
<option disabled="disabled" value="{{ $category->id }}" {{ $category->id == old('category_id') ? 'selected' : '' }}>{{ $category->name }}</option>
#if ($category->children)
#foreach ($category->children as $child)
#unless($child->name === 'Job Fundamentals')
<option value="{{ $child->id }}" {{ $child->id == old('category_id') ? 'selected' : '' }}> {{ $child->name }}</option>
#endunless
#endforeach
#endif
#endunless
#endforeach
</select>
</div>
</div>
<div class="col-12 col-sm-6">
<div class="form-group">
<label class="control-label"> Goal Title:<span style="color:red;">*</span></label>
<input type="text" name="goal_title" placeholder="Enter goal title here" class="form-control">
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<label>Goal Description</label>
<textarea rows="2" name="goal_description" class="form-control" placeholder="Enter Goal Description here ..."></textarea>
</div>
</div>
<div class="col-12 col-sm-4">
<div class="form-group">
<label class="control-label"> Weight:</label>
<input type="number" name="weighted_score" placeholder="Enter weighted score here" class="form-control">
</div>
</div>
</div>
</div>
</div>
<!-- /.card-body -->
<div class="card-footer">
<button type="submit" class="btn btn-primary">{{ trans('global.save') }}</button>
<button type="button" onclick="window.location.href='{{route('goals.index')}}'" class="btn btn-default">Cancel</button>
</div>
</form>
</div>
<!-- /.card -->
</div>
<!--/.col (left) -->
</div>
From the diagram above, GoalType (goal_type_id) dropdown contains all the children fields from goal_types. What I want to achieve is this:
When Goal Type is selected from the dropdown, the system goes to the goals table (Goal). It displays total weighted_score based on employee_id and goal_type_id.
When the user tries to enter data into weight text field (weighted_score), the application adds the value in the text field to the result in number one (1) above. If the result is more than the max_score in goal_types (GoalType) based on the parent max_score, then an error message is displayed.
How do I achieve this?
Thank you.
You have to use AJAX for this, if you're unfamiliar with it, you can read about it here: https://www.w3schools.com/xml/ajax_intro.asp
I would also use JQuery to make things simpler. This way, you can do like this:
When Goal Type is selected from the dropdown, the system goes to the goals table (Goal). It displays total weighted_score based on employee_id and goal_type_id.
here you have to send to backend the goal type the user selected, for that, set an on change event in the combobox that triggers the AJAX request:
$('#goal_type').change(request_goals($('#goal_type').val()))
And the request_goals function should be like this:
function request_goals(){
$.ajax({
method: "GET",
dataType: 'json',
url: /*YOUR CONTROLLER URL*/,
error: function(jqXHR, textStatus, errorThrown) {
console.log(errorThrown);
console.log("error");
},
success: function (response) {
/* HERE DO WHAT YOU NEED */
}
}
You will have to create a route and a controller function that returns the data you need.
When the user tries to enter data into weight text field (weighted_score), the application adds the value in the text field to the result in number one (1) above. If the result is more than the max_score in goal_types (GoalType) based on the parent max_score, then an error message is displayed.
Here you should do the same trick, set an event handler in the weighted_score field that sends an ajax request.
I hope it can help you.
If you want to archive it without AJAX calls, you can submit the form on select box change: <select name="goal_type_id" onchange="this.form.submit()">
In the controller you can catch the old input with old("goal_type_id"). You can query/calculate now the total weighted_score.

Laravel - How to Make Lesson aggregate score not more than course max score

I am developing a web application on Student Course Management using Laravel-5.8
Models
class Lesson extends Model
{
protected $table = 'Lessons';
protected $fillable = [
'lesson_name',
'course_id',
'student_id',
'score_obtained',
];
public function gradelevel()
{
return $this->belongsTo('App\Models\Course','course_id');
}
public function student()
{
return $this->belongsTo('App\Models\Student','student_id');
}
}
class Course extends Model
{
protected $table = 'courses';
protected $fillable = [
'course_code',
'course_name',
'max_score',
];
}
Controller
class LessonController extends Controller
{
public function create()
{
$courses = Course::all();
$students = Student::all();
return view('lessons.create')->with('courses', $courses)->with('students', $students);
}
public function store(StoreLessonRequest $request)
{
try {
$lesson = Lesson::create([
'lesson_name' => $request->lesson_name,
'course_id' => $request->course_id,
'lesson_id' => $request->lesson_id,
'score_obtained' => $request->score_obtained,
]);
Session::flash('success', 'Lesson is created successfully');
return redirect()->route('lessons.index');
} catch (Exception $exception) {
Session::flash('danger', 'Lesson creation failed!');
return redirect()->route('lessons.index');
}
}
}
create.blade
<form action="{{route('lessons.store')}}" method="post" class="form-horizontal" enctype="multipart/form-data">
{{csrf_field()}}
<div class="card-body">
<div class="form-body">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label>Course</label>
<select class="form-control select2bs4" data-placeholder="Choose Course" tabindex="1" name="course_id" style="width: 100%;">>
<option value="">Select Course</option>
#if($courses->count() > 0)
#foreach($courses as $course)
<option value="{{$course->id}}">{{$course->course_name}}</option>
#endforeach
#endif
</select>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label>Student</label>
<select class="form-control select2bs4" data-placeholder="Choose Course" tabindex="1" name="student_id" style="width: 100%;">>
<option value="">Select Student</option>
#if($students->count() > 0)
#foreach($students as $student)
<option value="{{$student->id}}">{{$student->student_name}}</option>
#endforeach
#endif
</select>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label>Lesson<span style="color:red;">*</span></label>
<input type="text" name="lesson_name" placeholder="Enter lesson here" class="form-control" value="{{old('lesson_name')}}">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label>Obtained Score<span style="color:red;">*</span></label>
<input type="text" name="score_obtained" placeholder="Enter score obtained here" class="form-control" value="{{old('score_obtained')}}">
</div>
</div>
</div>
</div>
</div>
<!-- /.card-body -->
<div class="card-footer">
<button type="submit" id="submit_create" class="btn btn-primary">Score</button>
</div>
</form>
Each core have maximum score allocated and this is done at the setting. For each course, students have lesson and scores are obtained for each lesson.
What I want to achieve is that on the lesson create form, what student score_obtained are being entered the application should sum up all the scores (from the lesson table and the just entered one) the student obtained for that particular course including the one in the textbox, if its more that what is in the max_score in the courses table for that particular course the application should display a message and shouldn't allow the user to submit.
How do I achieve this?
Thank you.
In store method, you can write below code:
$maxScore = Course::find($request->course_id)->max_score;
$scoresObtained = Lession::where('course_id' , $request->course_id)->sum('score_obtained');
$totalScore = intval($scoresObtained) + intval($request->score_obtained);
if($totalScore > $maxScore)
{
return redirect()->back()->withErrors([__('Total score exceeds max score for selected course')]);
}
You can use floatval() in place of intval() in case your scores are in decimals

Updating Item from Pivot Table Fields

I have item_color and item_size pivot tables, and would like to update my size and color fields using their field values, and an not exactly sure where to start. Here's what I have so far.
ItemSize.php
<?php
class ItemSize extends \Eloquent
{
protected $table = 'item_size';
protected $fillable = [];
public function item() {
return $this->belongsTo('Item');
}
}
ItemColor.php
<?php
class ItemColor extends \Eloquent
{
protected $table = 'item_color';
protected $fillable = [];
public function item() {
return $this->belongsTo('Item');
}
}
VendorController
public function postVendorUpdateItems ($id)
{
$input = Input::all();
$items = Item::find($id);
$validator = Validator::make($input,
[ 'item_name' => 'max:50',
'item_id' => 'max:50',
'normalprice' => 'numeric',
'karmaprice' => 'numeric',
'asin' => 'max:50',
]);
if($validator->passes())
{
$items->name = $input['item_name'];
$items->normalprice = $input['normalprice'];
$items->karmaprice = $input['karmaprice'];
$items->asin = $input['asin'];
$items->id = $input['item_id'];
$items->save();
return Redirect::route('account-vendor-index')
->with('global', 'You have updated your item.');
}
return Redirect::route('account-vendor-index')
->withErrors($validator)
->with('global', 'Your item could not be updated.');
}
View
<form class="form-horizontal" role="form" method="post" action="{{url('account/vendor/update/items')}}/{{$item->id}}">
<input type="hidden" id="brand_id" placeholder="brand_id" value="{{$brand->id}}" name="brand_id">
<input type="hidden" id="item_id" placeholder="item_id" value="{{$item->id}}" name="item_id">
<div class="form-group">
<label for="colors" class="col-xs-3 control-label">Colors</label>
<div class="col-xs-6">
<input type="text" class="form-control input-sm" id="colors" name="colors" placeholder="#foreach($item->colors as $color){{$color->color}}#endforeach" value="">
</div>
<button type="" class="btn btn-primary btn-sm">Add color</button>
<div class="clear"></div>
<div class="col-xs-offset-3 showColors">
#foreach($item->colors as $color)
{{$color->color}}
#endforeach
</div>
</div>
<div class="form-group">
<label class="col-xs-3 control-label">Sizes</label>
<div class="col-xs-9">
<select id="selectSizes" multiple="multiple" class="form-control selectSizes" name="sizes">
<option value="XS (0-2)">XS (0-2)</option>
<option value="S (4-6)">S (4-6)</option>
<option value="M (8-10)">M (8-10)</option>
<option value="L (12-14)">L (12-14)</option>
<option value="XL (16-18)">XL (16-18)</option>
<option value="XXL (20-22)">XXL (20-22)</option>
</select>
</div>
</div>
<div class="form-group bot-0">
<div class="col-xs-offset-3 col-xs-9">
<button type="submit" class="btn btn-main btn-sm">Save changes</button>
</div>
</div>
</form>
item_size and item_color are not pivot tables. They are in fact a one to one relationship with your schema. If want you truly want are pivot tables, you need to define a color and a size table, then create a many-to-many relationship. See here: https://laravel.com/docs/master/eloquent-relationships
Then to update related models, you'll use the attach, sync, detach, etc. methods.
Also, what version of Laravel are you running? If you're running 5.1+ look into form request validation. See here: https://laravel.com/docs/master/validation#form-request-validation
That will remove all the validation logic from your controller.

Laravel 4 - Update many to many

I am currently trying to amend an update page which features a many to many relationship. It updates to my database perfectly, however all I'm trying to achieve now is to actually show the already selected items in my multiple select list in my view.
It currently just shows the entire list of oilgas jobs, just not the currently selected ones which should come from the query in some way.
So, it currently looks like this:
I need it to look like this, if Instrument Technician was previously chosen.
The associated files are as follows:
MODELS
OilGasJob.php
<?php
class OilGasJob extends \Eloquent {
protected $table = 'oilgasjobs';
public function industryjobs()
{
return $this->belongsToMany('IndustryJob');
}
}
IndustryJob.php
<?php
class IndustryJob extends \Eloquent {
protected $table = 'industryjobs';
public function oilgasjobs()
{
return $this->belongsToMany('OilGasJob');
}
}
CONTROLLER (CREATE PAGE AND STORE)
public function edit($id)
{
$industryjob = IndustryJob::find($id);
if(is_null($id))
{
return Redirect::to('/admin/industry-jobs')->with('message', 'This division job is not valid');
}
View::share('page_title', 'Edit Division Job');
return View::make('admin/industry-jobs/edit')->with('industryjob',$industryjob);
}
public function update($id)
{
$rules = array(
'job_title' => 'Required|Min:3|Max:80'
);
$validation = Validator::make(Input::all(), $rules);
If ($validation->fails())
{
return Redirect::back()->withErrors($validation);
} else {
$industryjob = IndustryJob::find($id);
if(is_null($id))
{
return Redirect::to('/admin/industry-jobs')->with('message', 'This divison job is not valid');
}
View::share('page_title', 'Edit Division Job');
$industryjob->job_title = Input::get('job_title');
$industryjob->slug = Str::slug(Input::get('job_title'));
$industryjob->job_description = Input::get('job_description');
$industryjob->job_qualifications = Input::get('job_qualifications');
$industryjob->save();
$industryjob->oilgasjobs()->sync(Input::get('oilgasjobs'));
return Redirect::to('/admin/industry-jobs')->with('message', 'Division Job updated successfully');
}
}
VIEW
{{ Form::open(array('url' => URL::to('admin/industry-jobs/edit/'.$industryjob->id), 'class'=>'form-horizontal', 'method' => 'POST')) }}
<div class="form-group">
<label class="col-md-2 control-label" for="industry_name">Job Title (*)</label>
<div class="col-md-10">
<input class="form-control" type="text" name="job_title" id="job_title" value="{{ $industryjob->job_title }}" />
</div>
</div>
<!-- Industry Type -->
<div class="form-group">
<label class="col-md-2 control-label" for="body">Related Jobs in Oil & Gas</label>
<div class="col-md-10">
<select name="oilgasjobs[]" id="oilgasjobs[]" size="6" class="form-control" multiple>
#foreach(OilGasJob::orderBy('job_title', 'ASC')->get() as $oilgasjob)
<option value="{{ $oilgasjob->id }}" >{{ $oilgasjob->job_title }}</option>
#endforeach
</select>
</div>
</div>
<!-- ./ Industry Type -->
<!-- Form Actions -->
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="reset" class="btn btn-default">Reset</button>
<button type="submit" class="btn btn-success">Update Job</button>
</div>
</div>
<!-- ./ form actions -->
{{ Form::close() }}
In edit method add this code:
// ...
$linkedOilgasjob = DB::table('oilgasjob_industryjob')->lists('oilgasjob_id');
return View::make('admin/industry-jobs/edit'
,compact('industryjob')
,compact('linkedOilgasjob'));
Then in your view
<option value="{{ $oilgasjob->id }}" {{ in_array($oilgasjob->id,linkedOilgasjob) ? "selected='selected'" : "" }} >
{{ $oilgasjob->job_title }}
</option>
CONTROLLER
public function edit($id)
{
$data['industryjob'] = IndustryJob::find($id);
$data['oilgasjobs'] = DB::table('industry_job_oil_gas_job')->where('industry_job_id','=',$id)->lists('oil_gas_job_id');
if(is_null($id))
{
return Redirect::to('/admin/industry-jobs')->with('message', 'This division job is not valid');
}
View::share('page_title', 'Edit Division Job');
return View::make('admin/industry-jobs/edit', $data);
}
VIEW
<select name="oilgasjobs[]" id="oilgasjobs[]" size="6" class="form-control" multiple>
#foreach(OilGasJob::orderBy('job_title', 'ASC')->get() as $oilgasjob)
<?php
$selected = "";
if(in_array($oilgasjob->id, $oilgasjobs))
$selected = "selected";
?>
<option value="{{ $oilgasjob->id }}" <?php echo $selected; ?>>{{ $oilgasjob->job_title }}</option>
#endforeach
</select>

Resources