Create Job
Details
In accordance with Scrum-490, job input is no longer separated between Area Manager and Location Manager.
Contract
- URL: /api/portal/jodJob/storeJodJobForManager
- Method: POST
- Permission: create-new-a-job
- Payload
- location_id
- required
- integer
- exists
select * from locations
where
id = <location_id>
and status = 1
limit 1; - job_template_id
- integer
- exists
select * from job_templates
where
id = <job_template_id>
and status = 1
limit 1; - job_type_id
- required
- integer
- exists
select * from job_types
where
id = <job_type_id>
and status = 1
limit 1; - job_title
- required
- string
- max 100 chars
- min 2 chars
- job_description
- required
- string
- special_instructions
- nullable
- string
- hourly_rate
- required
- numeric
- job_start_date
- required
- date, with format:
Y-m-d H:i:s - after or equal current time
- job_end_date
- required
- date, with format:
Y-m-d H:i:s - after
job_start_date
- cash
- required
- 1 or 0
- bank_transfer
- required
- 1 or 0
- cheque
- required
- 1 or 0
- food_hygiene_certificate_required
- required
- 1 or 0
- date_of_week
- required
- comma separated
- must in :
monday,tuesday,wednesday,thursday,friday,saturday,sunday - will create slot array between
job_start_date&job_end_date
- required_certificates
- required
- json
- location_id
Payload example
"job_start_date": "2024-10-20 14:14:00"
"job_end_date": "2024-10-26 15:14:00"
"date_of_week": "monday,wednesday,friday,sunday"
"location_id": 8
"job_type_id": 1,
"job_title": "Waiter/Waitress"
"job_description": "Job Responsibilities:\n- Serve food and beverages\n- Table setting and maintain cleanliness environment\n- Deliver food to customers\n- Assist Manager/Supervisor in daily operational matters\n- To perform flyer distribution duties when necessary.\n\nAdditional Requirements:\n- Provide excellent service to customers\n- Friendly Personality\n- Must be able to speak English.\n\nDress Code:\n- Black polo shirt or t-shirt (plain)\n- Black pants / jeans\n- Dark covered shoes (with proper grip)\n\nPayment Instructions: \n- JobsOnDemand will pay the Member directly after the job has been completed. Payment will be processed on the next business day.\n- Please ensure that your bank account details are accurately provided.\n\nStaff meal will be provided for this job posting.\nspecial_instructions: SPECIAL INSTRUCTIONS\n- Please report to the restaurant manager upon arrival.\n- MANDATORY: Please bring along your NRIC or Student Pass for validation."
"hourly_rate": 8
"bank_transfer": 1
"cash": 0
"cheque": 0
"food_hygiene_certificate_required": 0
"job_template_id": 1
"user_location_id": 206
"required_certificates": "[{\"id\":1,\"checked\":true},{\"id\":2,\"checked\":false}]"
Flow
Queries
1. Validate location from payload
a. The query
This query call in App\Http\Requests\JodJob\StoreJodJobForManagerRequest
select *
from `locations`
where
`id` = ?
and `status` = 1
and `locations`.`deleted_at` is null
limit 1;
b. Query log time consumption
0.36 ms
c. Conclusion
Already optimized, because query using primary key
2. Get job template by job_template_id
a. The query
This query call in:
App\Http\Requests\JodJob\StoreJodJobForManagerRequestwhen validatingjob_location_idApp\Services\JodJobService::storeJodJobForManagerto validate withusers.company_id
select *
from `job_templates`
where
`id` = ?
and `status` = 1
limit 1;
b. Query log time consumption
0.58 ms
c. Conclusion
- Query already optimized, because query using primary key
- Code can optimize with just call single query when validate payload:
- Add jobTemplate property for
App\Http\Requests\JodJob\StoreJodJobForManagerRequestclassclass StoreJodJobForManagerRequest extends FormRequest
{
private $jobTemplate;
public function getJobTemplate()
{
return $this->jobTemplate;
}
// other code
} - Remove
JobTemplateEnableExistRulefromjob_template_idin rules()// before
// 'job_template_id' => [
// 'integer',
// new JobTemplateEnableExistRule
// ],
// after
'job_template_id' => [
'integer',
], - Add withVali
dator function, so after Laravel validaterules()`, it will run next validator/**
* @param Illuminate\Validation\Validator
*/
public function withValidator(Validator $validator)
{
if ($validator->fails()) {
return;
}
$validator->after(function ($validator) {
// get jobTemplate
$this->jobTemplate = app(JobTemplateRepository::class)
->where('id', $this->input('job_template_id'))
->where('status', 1)
// ->where('company_id', $this->user()->company_id)
->first();
if (empty($this->jobTemplate)) {
abort(422, trans('validation.exists', ['attribute' => 'job_template_id']));
} elseif ($this->jobTemplate->company_id != $this->user()->company_id) {
abort(422, trans('messages.portal.jobs.errors.area_user_is_not_allow_with_job_template'));
}
});
}
- Add jobTemplate property for
3. Validate job type from payload
a. The query
This query call in App\Http\Requests\JodJob\StoreJodJobForManagerRequest
from `job_types`
where
`id` = ?
and `status` = 1
limit 1;
b. Query log time consumption
0.32 ms
c. Conclusion
Already optimized, because query using primary key
4. Get current manager
a. The query
This query call in App\Services\BaseService::getCurrentManager, to validate is current user is area, location, or hiring manager
select *
from `users`
where
`id` = ?
and `user_type` in ('AREA', 'LOCATION', 'HIRING_MANAGER')
and `status` = 1
limit 1;
b. Query log time consumption
0.65 ms
c. Conclusion
Already optimized, because query using primary key
5. Get total complete job
a. The query
This query is called every time the User Model is retrieved from
App\Observers\UserObserver::retrieved
SELECT COUNT(job_user.job_id) as total_completed_job
FROM
users
inner join job_user on users.id = job_user.app_user_id
inner join jod_jobs on job_user.job_id = jod_jobs.id
WHERE
EXISTS (
SELECT slot_user.app_user_id
FROM
slots
INNER JOIN slot_user on slots.id = slot_user.slot_id
WHERE
slot_user.app_user_id = job_user.app_user_id
AND job_user.job_id = slots.jod_job_id
AND slot_user.status = 5
)
AND users.id = ?
AND jod_jobs.status IN (3, 16);
b. Query log time consumption
0.63 ms
c. Conclusion
Already optimized, because query using primary key
6. Check location with current user
a. The query
This query is executed in App\Services\JodJobService::storeJodJobForManager, it will get location by payload location_id and connect to the current user
select *
from `locations`
where
`id` = <location_id>
and exists (
select *
from
`users`
inner join `user_locations` on `users`.`id` = `user_locations`.`user_id`
where
`locations`.`id` = `user_locations`.`location_id`
and `user_id` = <current users.id>
)
and `locations`.`deleted_at` is null
limit 1;
b. Query log time consumption
0.49 ms
c. Conclusion
- Query already optimized, because query using primary key
- This should be combined with payload validation, and store this query in
App\Http\Requests\JodJob\StoreJodJobForManagerRequest
7. Get insurance rate
a. The query
select *
from `configurations`
where
`config_key` = 'portal.insurance_rate'
limit 1
b. Query log time consumption
0.34 ms
c. Conclusion
- Query already optimized, because query using primary key
- This should be combined with payload validation, and store this query in
App\Http\Requests\JodJob\StoreJodJobForManagerRequest
8. Save job
a. The query
insert into
`jod_jobs` (
`location_id`,
`job_template_id`,
`job_type_id`,
`job_title`,
`job_description`,
`hourly_rate`,
`job_start_date`,
`job_end_date`,
`cash`,
`bank_transfer`,
`cheque`,
`food_hygiene_certificate_required`,
`date_of_week`,
`job_insurance`,
`total_job_salary`,
`created_by`,
`company_id`,
`status`,
`updated_at`,
`created_at`
)
values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
9. Create slots
a. The query
insert into
`slots` (
`slot_start_date`,
`slot_end_date`,
`status`,
`jod_job_id`,
`updated_at`,
`created_at`
)
values
(?, ?, ?, ?, ?, ?);
10. Create billing
a. The query
insert into
`billings` (
`job_id`,
`location_id`,
`job_type_id`,
`time_in`,
`time_out`,
`total_hour`,
`total_jod_credit`,
`created_by`,
`updated_at`,
`created_at`
)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
11. Create audit
a. The query
Some of Laravel eloquent like App/Entities/JodJob or App/Entities/Slot, are implements OwenIt\Auditing\Contracts\Auditable and have trait \OwenIt\Auditing\Auditable. It means, every time there is create, updated, deleted, and restored as in config/audit.php. It will trigger to insert new row to audits table
insert into
`audits` (
`old_values`,
`new_values`,
`event`,
`auditable_id`,
`auditable_type`,
`user_id`,
`user_type`,
`tags`,
`ip_address`,
`user_agent`,
`url`,
`updated_at`,
`created_at`
)
values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
12. Get location from JodJob for clever type
a. The query
This query load from JodJob eloquent for clever type data
select
`id`,
`name`,
`address`,
`location_logo_filename`
from
`locations`
where
`locations`.`id` in (<jod_jobs.location_id>)
and `locations`.`deleted_at` is null
13. Get locations.users from JodJob for clever type
a. The query
This query load from JodJob eloquent for clever type data
select
`users`.`id`,
`users`.`first_name`,
`users`.`last_name`,
`users`.`email`,
`user_locations`.`location_id` as `pivot_location_id`,
`user_locations`.`user_id` as `pivot_user_id`,
`user_locations`.`created_at` as `pivot_created_at`,
`user_locations`.`updated_at` as `pivot_updated_at`
from
`users`
inner join `user_locations` on `users`.`id` = `user_locations`.`user_id`
where
`user_locations`.`location_id` in (<jod_jobs.location_id>)
14. Get company from JodJob for clever type
a. The query
This query load from JodJob eloquent for clever type data
select
`id`,
`name`,
`company_logo_filename`
from
`companies`
where
`companies`.`id` in (<jod_jobs.location_id>)
and `companies`.`deleted_at` is null
15. Get jobType from JodJob for clever type
a. The query
This query load from JodJob eloquent for clever type data
select * from `job_types` where `job_types`.`id` in (1)
16. Update user total job
a. The query
This query execute when query #5 is not same with users.total_completed_jobs
update
`users`
set
`total_completed_jobs` = ?,
`users`.`updated_at` = ?
where `id` = ?
17. Get HQ email template for job approval notification
a. The query
select *
from `notification_templates`
where
`purpose` = 'hq_manager_email_noti_job_approval'
and `type` = 1
and `status` = 1
and `lang` = 'en'
limit 1;
b. Query log time consumption
1.06 ms
c. Conclusion
This query is not optimize, because query condition is not indexed.
18. Get company HQ email for job approval notification
a. The query
select *
from `users`
where
`company_id` = <users.company_id>
and `user_type` = 'HQ'
and `status` = 1
limit 1;
b. Query log time consumption
0.89 ms
c. Conclusion
This query is already optimized, because company_id is already indexed.
19. Get all company SUPER_HQ_EXTERNAL email for job approval notification
a. The query
select *
from
`users`
inner join `user_company` on `users`.`id` = `user_company`.`user_id`
where
`user_company`.`company_id` = <users.company_id>
and `users`.`user_type` = 'SUPER_HQ_EXTERNAL'
and `status` = 1
and `user_setting_email_notification_status` = 1;
b. Query log time consumption
1.57 ms
c. Conclusion
This query is almost optimized, because company_id is already indexed. But user_company.user_id is not indexed.
20. Insert email sms notifications
a. The query
insert into
`email_sms_notifications` (
`job_id`,
`sender_id`,
`sender_user_type`,
`receiver_id`,
`receiver_user_type`,
`purpose`,
`subject`,
`type`,
`updated_at`,
`created_at`
)
values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
21. Get in application notification template by purpose
a. The query
select *
from `notification_templates`
where
`purpose` = 'applicant_push_noti_job_opening'
and `type` = 3
and `status` = 1
b. Query log time consumption
0.51 ms
c. Conclusion
This query is almost optimized, because company_id is already indexed. But user_company.user_id is not indexed.
Notes
Payload
- Fields:
location_id,user_location_id,job_template_id,job_type_id
Using a query to the database, but not stored inrequest. It should be stored in the request class, so the main code will not run the same query again. This will improve performance, because the same query is not running repeatedly. - job_title field using laravel
max&betweenvalidation, while themaxofbetweenlower thanmaxvalidation (255 chars). It should not usingbetween&maxsimultaneously. If usingmaxthen should usingminvalidation. - The
date_of_weekcolumn will create a set of slots for validation. Although this does not access the database, the results of the slots should be stored in the request, so they are not recreated in the main code.
Model observer
Every time User model retrieved, it will call Get total complete job .
This query should not be made an observer to be called every time a User model is received. Get total jobs should be used when needed. Moreover, if there is a change in the total_completed_jobs field, it will automatically update to the database.
If the total_completed_jobs field value must be the latest value for HTTP responses to users, then it should be called every time there is a user response. Or you can consider using a fractal transformer.
Redundant queries
At beginning of App\Services\JodJobService::storeJodJobForManager, after call validateManager then call getCurrentManager, where getCurrentManager is included in validateManager. It run same queries twice.
Instead do this:
$errCode = $this->validateManager();
if (!empty($errCode)) {
return ['status' => false, 'error' => $errCode];
}
$manager = $this->getCurrentManager();
we can do this:
$manager = $this->getCurrentManager();
if (empty($manager)) {
return ['status' => false, 'error' => 'manager_not_found'];
} else {
if (is_null($manager->company_id)) {
return ['status' => false, 'error' => 'manager_has_not_company'];
}
}
sidebar_label: 'Update Job'
Update Job
Details
In accordance with Scrum-490, job input is no longer separated between Area Manager and Location Manager.
Contract
- URL: /api/portal/jodJob/updateJodJobForManager
- Method: PUT
- Permission: update-a-job-by-manager
- Payload
- job_id
- required
- integer
- exists
select *
from `jod_jobs`
where
`id` = ?
and `jod_jobs`.`deleted_at` is null
limit 1; - location_id
- required
- integer
- job_template_id
- integer
- job_type_id
- required
- integer
- job_title
- required
- string
- max 100 chars
- min 2 chars
- job_description
- required
- string
- special_instructions
- nullable
- string
- hourly_rate
- required
- numeric
- job_start_date
- required
- date, with format:
Y-m-d H:i:s - after or equal current time
- job_end_date
- required
- date, with format:
Y-m-d H:i:s - after job_start_date
- cash
- required
- 1 or 0
- bank_transfer
- required
- 1 or 0
- cheque
- required
- 1 or 0
- food_hygiene_certificate_required
- required
- 1 or 0
- date_of_week
- required
- comma separated
- must in :
monday,tuesday,wednesday,thursday,friday,saturday,sunday - will create slot array between
job_start_date&job_end_date
- required_certificates
- required
- json
- job_id
Payload exaple
{
"job_id": 14760,
"location_id": 2,
"job_template_id": 2729,
"job_type_id": 1713,
"job_title": "Content Creator Night Shifting",
"job_description": "The content creator will be responsible for developing high-quality copy for websites, social media, marketing materials, and other platforms.",
"special_instructions": "To do so, they will learn all about and make significant contributions to [Company X]'s branding and style guide to efficiently maximize the impact of content they generate.",
"hourly_rate": 24.00,
"job_start_date": "2024-10-31 18:00:00",
"job_end_date": "2024-11-10 06:00:00",
"cash": 0,
"bank_transfer": 1,
"cheque": 0,
"food_hygiene_certificate_required": 0,
"date_of_week": "monday,tuesday,friday,sunday",
"required_certificates": "[{\"id\":1,\"checked\":false},{\"id\":2,\"checked\":true}]"
}
Flow
Queries
1. Get jod_job by id
a. The query
This query call in App\Http\Requests\UpdateJodJobForManagerRequest and App\Services\JodJobService::updateJodJobForManager
select *
from `jod_jobs`
where
`id` = 14760
and `jod_jobs`.`deleted_at` is null
limit 1;
b. Query log time consumption
0.68 ms
c. Conclusion
- Already optimized, because query using primary key
- This query run multiple times, should save it in Laravel request after validation
2. Get current manager
a. The query
This query call in App\Services\BaseService::getCurrentManager, to validate is current user is area, location, or hiring manager
select *
from `users`
where
`id` = ?
and `user_type` in ('AREA', 'LOCATION', 'HIRING_MANAGER')
and `status` = 1
limit 1;
b. Query log time consumption
0.34 ms
c. Conclusion
Already optimized, because query using primary key
3. Get job_type by payload job_type_id
a. The query
select * from `job_types` where `id` = <job_type_id> and `status` = 1 limit 1
b. Query log time consumption
2.63 ms
c. Conclusion
Already optimized, because query using primary key
4. Get location by id
a. The query
select *
from `locations`
where
`id` = ?
and `status` = 1
and `locations`.`deleted_at` is null
limit 1;
b. Query log time consumption
1.48 ms
c. Conclusion
Already optimized, because query using primary key
5. Get users from location
a. The query
select
`users`.*,
`user_locations`.`location_id` as `pivot_location_id`,
`user_locations`.`user_id` as `pivot_user_id`,
`user_locations`.`created_at` as `pivot_created_at`,
`user_locations`.`updated_at` as `pivot_updated_at`
from
`users`
inner join `user_locations` on `users`.`id` = `user_locations`.`user_id`
where
`user_locations`.`location_id` = ?
b. Query log time consumption
1.59 ms
c. Conclusion
Already optimized, because query using index key
6. Get job certifications
a. The query
select
`certificates`.*,
`jod_job_certificates`.`jod_job_id` as `pivot_jod_job_id`,
`jod_job_certificates`.`certificate_id` as `pivot_certificate_id`,
`jod_job_certificates`.`status` as `pivot_status`,
`jod_job_certificates`.`created_by` as `pivot_created_by`,
`jod_job_certificates`.`updated_by` as `pivot_updated_by`,
`jod_job_certificates`.`created_at` as `pivot_created_at`,
`jod_job_certificates`.`updated_at` as `pivot_updated_at`
from
`certificates`
inner join `jod_job_certificates` on `certificates`.`id` = `jod_job_certificates`.`certificate_id`
where
`jod_job_certificates`.`jod_job_id` = ?
b. Query log time consumption
1.61 ms
c. Conclusion
Already optimized, because query using primary key
7. Update jod_jobs
a. The query
update
`jod_jobs`
set
`job_title` = ?,
`job_end_date` = ?,
`food_hygiene_certificate_required` = ?,
`total_job_salary` = ?,
`job_insurance` = ?,
`date_of_week` = ?,
`jod_jobs`.`updated_at` = ?
where
`id` = ?;
8. Create audit
a. The query
Some of Laravel eloquent like App/Entities/JodJob or App/Entities/Slot, are implements OwenIt\Auditing\Contracts\Auditable and have trait \OwenIt\Auditing\Auditable. It means, every time there is create, updated, deleted, and restored as in config/audit.php. It will trigger to insert new row to audits table
insert into
`audits` (
`old_values`,
`new_values`,
`event`,
`auditable_id`,
`auditable_type`,
`user_id`,
`user_type`,
`tags`,
`ip_address`,
`user_agent`,
`url`,
`updated_at`,
`created_at`
)
values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
9. Soft delete slots
a. The query
update
`slots`
set
`deleted_at` = ?,
`slots`.`updated_at` = ?
where
`slots`.`jod_job_id` = ?
and `slots`.`jod_job_id` is not null
and `slots`.`deleted_at` is null
10. Insert new jod_jobs slots
a. The query
insert into
`slots` (
`slot_start_date`,
`slot_end_date`,
`status`,
`jod_job_id`,
`updated_at`,
`created_at`
)
values
(?, ?, ?, ?, ?, ?)
sidebar_label: 'List Job'
List Job
Description
List of JOD jobs
Contract
- URL: /api/portal/jodJob/indexJobPosting
- Method: GET
- Permission: list-jod-jobs
- Request:
- limit
- integer
- page
- integer
- location_id
- integer
- min_wage
- numeric
- if max_wage exists, this value must lower than max_wage
- max_wage
- numeric
- if min_wage exists, must greater than min_wage
- job_start_date
- date
- date_format:
Y-m-d
- job_end_date
- date
- date_format:
Y-m-d - after or equal
job_start_date
- sort_field
- in:
- created_at
- job_start_date
- job_end_date
- job_title
- status
- no_users_apply_job
- no_users_selected
- hourly_rate
- location_name
- applicants
- in:
- sort_order
- asc or desc
- filter_types
- string comma separated (numeric)
- job jod status
- key_word
- string
- job_with_applicants
- 1 or 0
- limit
Response
200 status response example
{
"data": {
"total": 4494,
"limit": 10,
"page": 1,
"last_page": 450,
"data": [
{
"id": 111,
"job_type_id": 3,
"job_title": "Table Cleaner (Weekday)",
"location_id": 1,
"user_location_id": 86,
"job_start_date": "2017-07-16 12:00:00",
"job_end_date": "2017-07-16 18:00:00",
"hourly_rate": 10,
"no_users_selected": 1,
"status": 16,
"created_at": "2017-07-14T21:37:53.000000Z",
"no_users_apply_job": 0,
"job_type": {
"id": 3,
"title": "b4grfs2gddt",
"hex_color": null,
"logo_url": "http://127.0.0.1:8000/images/job_type_images/default.jpeg"
},
"location": {
"id": 1,
"name": "Fairprice Hyper Parkway Parade test",
"location_logo_filename": "110822125947.png",
"url_logo": "http://127.0.0.1:8000/storage/outlet_logo/thumb/110822125947.png",
"location_badge_assignments": []
},
"applicants": [
{
"id": 160,
"first_name": "JG",
"last_name": "Dicalp",
"country_code": "65",
"contact_number": "96966229",
"identity_status": "1",
"total_completed_jobs": 5,
"updated_at": "2024-11-07T07:37:51.000000Z",
"full_name": "JG Dicalp",
"pivot": {
"job_id": 111,
"app_user_id": 160,
"created_at": "2017-07-14T21:47:58.000000Z",
"updated_at": "2017-07-16T11:00:01.000000Z"
}
}
]
},
...
{
"id": 799,
"job_type_id": 5,
"job_title": "Retail Assistant",
"location_id": 1,
"user_location_id": null,
"job_start_date": "2021-11-18 11:11:00",
"job_end_date": "2021-11-18 18:11:00",
"hourly_rate": 8.5,
"no_users_selected": 0,
"status": 3,
"created_at": "2021-11-17T18:10:16.000000Z",
"no_users_apply_job": 0,
"job_type": {
"id": 5,
"title": "lt5rot2vejj",
"hex_color": null,
"logo_url": "http://127.0.0.1:8000/images/job_type_images/default.jpeg"
},
"location": {
"id": 1,
"name": "Fairprice Hyper Parkway Parade test",
"location_logo_filename": "110822125947.png",
"url_logo": "http://127.0.0.1:8000/storage/outlet_logo/thumb/110822125947.png",
"location_badge_assignments": []
},
"applicants": []
}
]
},
"message": "Retrieved successfully"
}
Queries
TBA