In my app, after a jobseeker reviews for an exam, a new jobseekerExamReview instance is created and stored to the $examReview variable to store their score. They are then redirected to a new page that displays the results of said exam review, so I passed $examReview as a parameter to the results route. The id gets displayed in the link and works, but the content of the exam won't display as values are apparently null. I checked all the columns of the new record and they all have data. When I finally performed dd on $examReview it returns empty.
Value of $examReview (Controller):
// create new Exam review session
$examReview = jobseekerExamReview::create([
'jobseeker_id' => $jobseeker->id,
'exam_id' => $exam_id
//upon creation 'results' are set to 0 at default and updated later after score calculation (see full code)
]);
Redirect in Controller:
// return view to review results with answered and correct
return redirect()->route('showResults', [$examReview]);
web.php
Route::get('/exam/reviewExam/results/{jobseekerExamReview:id}', [reviewExamController::class, 'showResults'])
->middleware(['auth','verified'])
->name('showResults');
Full code in controller:
public function calculateResult()
{
// get needed details
$jobseeker = Jobseeker::findorFail(auth()->user()->id);
$exam_id = request()->input('exam_id');
$jobApplication = jobApplication::find(session('jobApplicationId'));
$correctAnswers = 0;
// create new Exam review session
$examReview = jobseekerExamReview::create([
'jobseeker_id' => $jobseeker->id,
'exam_id' => $exam_id
]);
// loop through each question taken by user
foreach(request()->input('taken_questions') as $key => $question_id){
// is answer correct or not
// echo "<br>Question ID: ".$question_id;
$status = 0;
//answer[questionID] => answer_id
// if the answer[questionID] is not emptu and matched answer ID is correct
// echo "<br>Answer ID:".request()->input('answer.'.$question_id);
if((request()->input('answer.'.$question_id) != null) && (examAnswer::find(request()->input('answer.'.$question_id))->isCorrect == true)){
// answer is correct
// echo "is Correct";
$status = 1;
$correctAnswers++;
// create review Exam Answers
jobseekerExamReviewAnswer::create([
'exam_review_id' => $examReview->id,
'question_id' => $question_id,
'answer_id' => request()->input('answer.'.$question_id),
'isCorrect' => $status
]);
}
}
// calculate score
$percentScore = $correctAnswers/count(request()->input('taken_questions'))*100;
// update score in review
$examReview->update(['result' => $percentScore]);
// return view to review results with answered and correct
return redirect()->route('showResults',[$examReview]);
}
public function showResults(jobseekerExamReview $examReview)
{
return view('exams.exam-review-results',[
'examReview' => $examReview
])->with('reviewExamAnswers');
}
You have to have the name of the typehinted variable in your method signature (route action) matching the name of the route parameter in the route definition. Your route parameter is named jobseekerExamReview but the parameter to your Controller method is named jobseeker, which does not match. Because of this you are getting a new non existing instance of jobseekerExamReview injected (Dependency Injection) instead of Route Model Binding. You should adjust your route parameter to match:
Route::get('/exam/reviewExam/results/{examReview:id}', ...);
This would cause Implicit Route Model Binding to happen instead of Dependency Injection.
"Laravel automatically resolves Eloquent models defined in routes or controller actions whose type-hinted variable names match a route segment name."
Laravel 8.x Docs - Routing - Route Model Binding - Implicit Binding
As a side note, you should get in the habit of using an associative array when passing parameters to the URL helpers, like route, so it knows exactly what parameters you are passing:
return redirect()->route('showResults', ['examReview' => $examReview]);
Related
I create one quiz app. In which students attend the question and click on the next button next question appears. now the issue is that the student is on the second question and the URL like "takeQuizStudent/149?page=2" and he manually changes URL to "takeQuizStudent/149?page=1". so it should not happen. Once clicks on the next question he can't go back to the previous question.
You should check request page number from session in your controller,for example
public function show(Request $request, $id)
{
$value = $request->session()->get('pageNumber'); //2
//$id = 1
if($id < $value){
return response("invalid request",504);
}
//else update pageNumber value and do continue
// Via a request instance...
$request->session()->put('pageNumber', '3');
// Via the global helper...
session(['pageNumber' => '3']);
.....
}
Determining If An Item Exists In The Session
To determine if an item is present in the session, you may use the has method. The has method returns true if the item is present and is not null:
if ($request->session()->has('pageNumber')) {
//
}
To determine if an item is present in the session, even if its value is null, you may use the exists method. The exists method returns true if the item is present:
if ($request->session()->exists('pageNumber')) {
//
}
I have a table/model that contains multiple albums per user.
Is there a way to say that the column title should be unique, but only for the rows that have the same user_id?
Example: http://pastebin.com/8dvM4a1T
As you can see in the example, the user with the id of 2 has created 2 albums, with the same title. I don't want that to be allowed, that's why I'm wondering if there's a way to deny that with the validator from Laravel?
I tried this, but that did not work.
// Validator
$validator = Validator::make($input, [
'title' => 'required|min:1|max:255|unique:galleries,title,'. Auth::user() -> id .',user_id',
'description' => 'min:1|max:255'
]);
Any help appreciated, thanks.
Your code should be something like:
'title' => 'unique:galleries,title,NULL,id,user_id,'.Auth::user() -> id.'',
Or, you can write a custom rule
Reference here
The approach with the default unique rule does not work because the rule expects the column value to be passed as the third parameter, so in your case it would check if the title column is equal to the Auth::user()->id value which is not what you want.
You can create you own custom validation rule by adding the following code to the boot method of the App\Providers\AppServiceProvider class:
Validator::extend('unique_custom', function ($attribute, $value, $parameters)
{
// Get the parameters passed to the rule
list($table, $field, $field2, $field2Value) = $parameters;
// Check the table and return true only if there are no entries matching
// both the first field name and the user input value as well as
// the second field name and the second field value
return DB::table($table)->where($field, $value)->where($field2, $field2Value)->count() == 0;
});
Now you can use the unique_custom (or you can name it whatever you like) rule like so:
$validator = Validator::make($input, [
'title' => 'required|min:1|max:255|unique_custom:galleries,title,user_id,' . Auth::id(),
'description' => 'min:1|max:255'
]);
The rule expects the parameters to be the following:
the 1st parameter to be the table name, which in this case is galleries
the 2nd parameter to be the field name that is supposed to be unique and for which the value comes from the user input, which in this case is title
the 3rd parameter to be the second field name that will be added to the query conditions, which in this case is user_id
the 4th parameter to be the value of the field name passed as the third parameter
Also you can use Auth::id() since that is the short form of Auth::user()->id.
You can read more about Custom Validation rules in the Laravel Documentation.
Laravel 5.3 and above
use Illuminate\Validation\Rule;
'email' => Rule::unique('galleries')->where(function ($query) {
$query->where('user_id', Auth::id());
})
Laravel 9
use Illuminate\Validation\Rule;
Rule::unique('galleries')->where(fn ($query) => $query->where('user_id', Auth::id()))
I've got problem with field validation.
I would like to validate form through model. I want to check if field with some value exists.
I would like to block using some titles more than once.
For example
if field "Site" with title "Main" exists in database, you can't validate form.
If it doesn't exist, you can pass it.
I would like to allow user to add just one "Site" with title "Main", but he can add "Site" with any other title in any case.
Have you got some idea how to solve it?
I think you have two options.
(1) Setup an Ajax request to the server.
To do so:
Create a function, that responds to an Ajax request, in your SiteController named checkName()
public function checkName($name) {
// allow ajax requests
$this->request->allowMethod(['ajax']);
// perform your check within the db
$isExistent = [...];
// prepare the response
$response = ['name' => $name, 'isExistent' => $isExistent];
if ($this->request->isAjax()){
$this->autoRender = false;
$this->response->disableCache();
$this->response->type(['json' => 'application/json']);
$this->response->body(json_encode($response));
}
}
Add the route to your routes file with the option '_ext' => 'json'
Prepare your Javascript Ajax function that call the route you have defined and attach it on the onchange attribute of your input field. (see this link for a simple example: http://www.w3schools.com/jquery/ajax_ajax.asp)
(2) Make the 'name' field of the Site table unique.
To do so you could add the following function to your SiteTable class
public function buildRules(
RulesChecker $rules
) {
$rules->add($rules->isUnique(['name']));
return $rules;
}
This is one of my first applications out of tutorials so I don't know how to express my issue well.
Well I have these 2 tables:
User ( id, code )
Hours ( id, user_id, created)
I want to know how I can add an entry to the Hours table using the user_code.
I tried to grab the data of the User table with the code value and then findBy and pass for the patchEntity but it did not work.
I don't have a whole lot of information to work with, but I'll give it a go.
I want to know how I can add an entry to the Hours table using the
user_code
You mention using patchEntity, so that's updating information that's already there. Assuming user_code is the 'code' column you're talking about there, first find the user by his code:
$users_tbl = TableRegistry::get('Users');
// find the user
$user = $users_tbl->findByCode($user_code)->first();
if ($user) {
// replace '$this->request->data() with whatever patch data you wanted
$users_tbl->patchEntity($user, $this->request->data(), [
'associated' => ['Hours']
]
if ($users_tbl->save($user)) {
// success!
} else {
// error!
}
} else {
// error!
}
It will also depend on how you have the data you passed in (where my '$this->request->data() is, or whatever your array might be) - it needs to match the right column names and be in the correct format listed here.
However, this is updating the data. Just adding the data, you can load the hours table and add a new entry with the user_id acquired from the user search:
$hours_tbl = TableRegistry::get('Hours');
$hours = $hours_tbl->newEntity([
'user_id' => $user->id // $user populated from same method earlier
]);
/* assumed 'id' was autoincrementing and 'created' was populated
through Timestamp behavior */
if ($hours_tbl->save($hours)) {
// yay!
} else {
// boo
}
I am trying to understand how sorting works in GridView by means of generating a default CRUD application. Sorting happens after clicking the respective attribute which is the table header. The column name is attached to the url with the variable sort and on click the action method is invoked, but what I am wondering is that the action method which is mentioned in the url with the actual variable $sort is not present in the controller.
Below is a example
The url looks like the below,
/advanced/frontend/web/index.php?r=site%2Findex&sort=customer_user_name2
But there is no corresponding action method in the site controller as
function actionIndex($sort);
Consider following example
We have Passenger CRUD
Consider Passenger - a model and PassengerSearch - its corresponding search model
Passenger attribute are id, name and country_name
In PassengerController.php
<?php
...
public function actionIndex()
{
$searchModel = new PassengerSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
...
?>
Search string a in name column
and sort id column in desc order
Look at the url generated, it would be like
`http://localhost/YII2proj/passenger/index?PassengerSearch%5Bid%5D=&PassengerSearch%5Bname%5D=a&PassengerSearch%5Bcountry_name%5D=&sort=-id`
In human readable format
http://localhost/YII2proj/passenger/index?
PassengerSearch[id]=&
PassengerSearch[name]=a&
PassengerSearch[country_name]=&
sort=-id
Observe PassengerSearch is a array. Please refer - How to send a array in url request?
Focus on the parameter to search() method of object PassengerSearch class it is Yii::$app->request->queryParams
Before
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
line, insert following code
echo '<pre>';
print_r(Yii::$app->request->queryParams);
echo '</pre>';
die;
and see the result
Array
(
[PassengerSearch] => Array
(
[id] =>
[name] => a
[country_name] =>
)
[sort] => -id
)
If no searching sorting done, then this array will be empty
So this line does your code like following
<?php
if(isset($_GET['sort']) || isset($_GET['PassengerSearch']['id'] ...)
{
// validate it because what if I changed url like
// `http://localhost/YII2proj/passenger/index?PassengerSearcasdh[dfsid]=&PassengerSearch[name]=a&PassengerSearch[country_name]=&sorsfdft=-idadlashdfhls`
// ie some scrap value
// If error returned from validate function, default sorting is done, and malformed search param is not set
// if success, fill up that array
}
?>
Then in search Model load() is the hero.
Where and how above code is done? - Refer Model and load()
Searching is done via andFilterWhere after loading ie load() and that you can see in search() method
Hope you got the idea behind searching and sorting.