Check if model belongs to other model - laravel

In my application, I have users that are connected to businesses. These businesses have job offers. When a user wants to edit a business' job offer, the url will be like /business/foo/job/bar. When I change the business variable to the name of a different one, like this: /business/other-business/job/bar I still get the job called bar even though it does not belong to the business. I tried to use gates to check if the job offers belong to the business but it didn't work. Below is the code for the edit function which shows the edit page.
public function edit(Business $business, Job $job)
{
return view('dashboard.business.jobs.edit', [
'business' => $business,
'job' => $job,
]);
}
I can add the following code to all the functions but it is not very pretty
if($business->id !== $job->business->id) { return abort(404); }
I was wondering if there is a better solution to this problem.

Well you can choose between
if($business->jobs->contains($job->id)) return redirect()->to("somewhere");
or
if($job->business->is($business)) return redirect()->to("somewhere");
The second one is more efficient because you have to retrive from the database just one record to check if they are correlated, the business record, where the first one instead you have to retrive all the jobs of that business.
Those solution in my opinion are actually very clear, you can literally read them and understand what you are doing.
Also if you want just one line of code, you can do this:
public function edit(Business $business, Job $job)
{
return
$job->business->is($business)
?
view('dashboard.business.jobs.edit', [
'business' => $business,
'job' => $job,
])
:
return redirect()->to("somewhere");
}

Related

Working with database parameters in session variables

I'm doing a project where I work with multiples databases.
The databases are dynamic, so in a general table (in my project database) I keep the names of the databases with which to work. On the initial screen (after login) I show the databases to choose which one to work with (multi-store site type, you choose the store and enter with its database). I do this by setting a session variable in the controller:
public function shopselect($id) {
if ($id != 0)
{
$shop = Shop::find($id);
Config::set('database.connections.tienda.database', $shop->database);
DB::reconnect('tienda');
Session::put('shop', $shop->id);
Session::put('shopname', $shop->name);
Session::put('shopdatabase', $shop->database);
}
else
{
Session::remove('shop');
Session::remove('shopname');
Session::remove('shopdatabase');
}
return redirect(route('dashboard'));
}
I call this function from two places.
First: in the view, in a dropdown where I show the user's data to choose another store, what I do is eliminate the variables.
Seleccionar otra tienda
Logically I declared this route in the routes file
Route::get('shopselect/{shop}', [App\Http\Controllers\ShopController::class, 'shopselect'])->name('shopselect');
Now, in the models, I have this constructor:
public function __construct()
{
Config::set('database.connections.tienda.database', Session::get('shopdatabase'));
DB::reconnect('tienda');
}
And in the Form Request I repeat those two lines of code, where I need to retrieve data from the database, for example for the records that must be 'unique'
public function rules()
{
Config::set('database.connections.tienda.database', Session::get('shopdatabase'));
DB::reconnect('tienda');
if ($this->method() == 'PUT')
{
return [
'name' => 'required',
'dni' => 'nullable|unique:tienda.customers,dni,'.$this->customer,
'address' => 'string|nullable',
...
I think I could simplify it by setting a Config::set to the database parameters, but would that work for multiple users? or the last one chosen by the database will save these parameters in the configuration file?

Refactoring a Laravel aplication layers

My Laravel project starts to grow up and I'm starting to dealing with fat Controllers and Models. Probably I'm for away from the SOLID principles path and my code is not DRY.
I decided to refactor my layers logic to follow more SOLID and try to be more DRY. After some research over the internet I ended up with the following conclusion:
Let me explain this diagram:
First, we start with a view, where the user performs some action and the request is sent to the Controller.
Controller responsibility is to handle the request and give the response to the user. It will be able to call 3 different layers (blue numbers):
Service: to handle business logic like calculations, special actions, etc.
Repository: where all query logic will be placed. For example, if on index method we want to return the users list with users that have more than 100 posts and are ordered by name (Example 1).
Laravel Resource (Transformers): with the responsibility to transform a model into JSON. When we change our table we don't have to change all the views and controllers or models affected by that change. It will be all done in one place.
Example 1:
# UserController.php
public function index()
{
$users = new UserCollection($this->UserRep->usersWithPost());
return view('user-list', compact('users'));
}
# UserRepository.php
public function usersWithPost($min = 100)
{
return $this->model->where('post_count', '>=', $min)->orderBy('name');
}
# UserResource.php
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'post_count' => $this->post_count,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
Service calls (green numbers):
It may call Repository if it needs any data from my model to perform some action.
It also may call Laravel Resource "Transformers" if there were repository calls.
The repository will use the eloquent model to persist query on my data storage (MySQL).
This is how I plan to refactor my code, however, I do not have experience with Laravel and I would like to ask more experienced developers if they can indicate me if I'm on the right path.
Do you think it makes sense and it is a good practice?
I would like to highlight that I will not switch between ORMs. I will use eloquent with MySQL as my data storage, that's why I'm planning to put all my queries to repositories to have a different layer for queries logic.

Creating new 'belongs-to' relation in Laravel with form requests

I am new to Laravel and can't quite wrap my head around the Form Requests and how to use them. In simple cases, it's simple enough but what I need is a conditional creation of a related model before progressing with the rest of the request. Let me explain.
//Job model
job_id PK
client_id
some_field
//Client model
client_id
external_id
name
Now in my Create Job interface, I have a Select2 combo box that uses AJAX to search 2 different sources and can produce 3 different results.
So, say I am creating new Job and can have a POST looking like any of these:
Script found a Client with id 21, we just want to save, it all is simple
'client_id' => 21
'some_filed' => Whatever
OR
Script didn't find a Client but we did a search of external API and returned this Identifier, which I can then parse to create a new Client. This needs to be done before I save Job as I need to have a client_id ready for it
'client_id' => '196c3c7e34cde1d4593391ddf1901fd7'
'some_filed' => Whatever
OR
Script finds neither local Client nor a remote datapoint so we want to create a new Client using just the name provided. Of course this has to happen before saving the Job
'client_id' => 'My new client name'
'some_filed' => Whatever
Where do I perform all this functionality?
My first procedural guess would be to stick $data['client_id'] = Client::HandleClientAndReturnId($data['client_id']) in StoreJob's validationData() before returning the result.
But how would I handle/report possible validation issues with creating new Client and how to manage transaction - I don't want to create a Client if validation of Job fails.
I am using Form Requests and actually am parsing several models in the request already, but those are HAS_ONE type of relationships so I create them after the Job. Here I need to reverse the process. Here is what I have in my controller, if that is of any use:
class JobController extends Controller
{
public function store(StoreJob $request, StoreJobDataAdd $request_2, StoreJobDataSup $request_3)
{
$job = Job::create($request->validated());
if ($_POST['add_on']) {
$job->dataAdd()->create($request_2->validated());
}
if ($_POST['sup_on']) {
$job->dataSup()->create($request_3->validated());
}
return new JsonResponse([
'success' => true,
'message' => __('New Job has been created.')
]);
}
}
Here are my relations:
class Job extends Model
{
public function dataAdd()
{
return $this->hasOne(JobDataAdd::class, 'job_id', 'job_id');
}
public function dataSup()
{
return $this->hasOne(JobDataSup::class, 'job_id', 'job_id');
}
public function client()
{
return $this->belongsTo(Client::class, 'contact_id', 'contact_id_client');
}
}
I have a feeling I am missing the right way to do it but not quite sure where to look for the answers so please point me in the right direction.
Clarification:
The objective of the whole exercise is to allow users to create new Clients or select existing by using one combobox. It's a Select2 box. For unfamiliar - a dropdown replacement that has an ability to free-type a string that is used to search for existing records and if not found it sends to the server what user typed in. Something like a dynamic dropdown with ability to search and add options by the user.

Where to apply custom middleware for authentication Laravel 5.2

In my project two type of users, Admin and Normal User. They are identified by the field isAdmin in users table. User can edit their profile by using the function below
public function userEditprofile(){
$user_detail = userDetail::find(Auth::user()->id);
$user_detail->address = Input::get('address');
.......
$user_detail->save()
return Redirect::route('showUserProfile');
}
and route is
Route::group(['middleware' => 'my_profile'], function() {
Route::get('/editprofile', array('uses' => 'UserController#userEditprofile', 'as' => 'userEditprofile'));
});
Admin can also edit any users profile by using
public function adminEditUserprofile($user_id){
$user_detail = userDetail::find($user_id);
$user_detail->address = Input::get('address');
.......
$user_detail->save()
return Redirect::route('showUserProfile', $user_id);
}
In both cases action is same but in first method there is no parameter is required. But in the case of admin , a parameter is required. Can I optimize the code by using any other way? I am a self learned programmer. I am not much aware of efficient programming methods.Can anyone reply?
I believe you should rather implement role based authorization. A good example has already been implemented by rappasoft/laravel-5-boilerplate
You should actually prefer this boilerplate as it includes a lot that is necessary for most of business applications.

How to avoid errors in saving data - Cakephp

I'm using Cakephp and trying to put in a method to make sure our reservation system doesn't let two users book the same appointment. Ex. User 1 opens the appointment, and User 2 opens it simultaneously. User 1 books the appointment. User 2 tries to book it but the system checks and sees it is no longer available.
I imagine this would take place in validation, or in a beforeSave(), but can't figure out how to do it.
Right now I made a function in the model to call from the controller. In the controller I have:
if ($this->Timeslot->checkIfNotAvailable()) {
$this->Session->setFlash('This timeslot is no longer available');
$this->redirect(array('controller' => 'users', 'action' => 'partner_homepage'));
}
and in the model I have this function:
function checkIfNotAvailable($data) {
$this->recursive = -1;
$timeslot = $this->find('all', array(
'conditions' => array(
'Timeslot.id' => $this->data['Timeslot']['id'])
)
);
if ($timeslot['student_id'] == 0) {
//They can reserve it, do not spring a flag
return false;
} else {
//Throw a flag!
return true;
}
}
I think I'm mixed up using custom validation when it's not called for. And it's not working obviously. Any suggestions?
Thanks!
If what you have is working, you can stick with it, you could also try creating a beforeValidate() call back function in your Model.
class YourModel extends AppModel {
function beforeValidate(){
if( !$this->checkIfNotAvailable( $this->data ) ) {
unset($this->data['YourModel']['time_slot']);
}
return true; //this is required, otherwise validation will always fail
}
}
This way you remove the time_slot before it goes to validation and it will drop a validation error at that point, kicking the user back to the edit page and getting them to pick a different time slot, ideally the updated data entry page will no longer have the used time slot available.

Resources