Skip to main content

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
    1. location_id
      1. required
      2. integer
      3. exists
         select * from locations
      where
      id = <location_id>
      and status = 1
      limit 1;
    2. job_template_id
      1. integer
      2. exists
      select * from job_templates
      where
      id = <job_template_id>
      and status = 1
      limit 1;
    3. job_type_id
      1. required
      2. integer
      3. exists
      select * from job_types
      where
      id = <job_type_id>
      and status = 1
      limit 1;
    4. job_title
      1. required
      2. string
      3. max 100 chars
      4. min 2 chars
    5. job_description
      1. required
      2. string
    6. special_instructions
      1. nullable
      2. string
    7. hourly_rate
      1. required
      2. numeric
    8. job_start_date
      1. required
      2. date, with format: Y-m-d H:i:s
      3. after or equal current time
    9. job_end_date
      1. required
      2. date, with format: Y-m-d H:i:s
      3. after job_start_date
    10. cash
      1. required
      2. 1 or 0
    11. bank_transfer
      1. required
      2. 1 or 0
    12. cheque
      1. required
      2. 1 or 0
    13. food_hygiene_certificate_required
      1. required
      2. 1 or 0
    14. date_of_week
      1. required
      2. comma separated
      3. must in : monday, tuesday, wednesday, thursday, friday, saturday, sunday
      4. will create slot array between job_start_date & job_end_date
    15. required_certificates
      1. required
      2. json

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

Backend Jod Job

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\StoreJodJobForManagerRequest when validating job_location_id
  • App\Services\JodJobService::storeJodJobForManager to validate with users.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\StoreJodJobForManagerRequest class
      class StoreJodJobForManagerRequest extends FormRequest
      {
      private $jobTemplate;

      public function getJobTemplate()
      {
      return $this->jobTemplate;
      }
      // other code
      }
    • Remove JobTemplateEnableExistRule from job_template_id in rules()
      // before
      // 'job_template_id' => [
      // 'integer',
      // new JobTemplateEnableExistRule
      // ],

      // after
      'job_template_id' => [
      'integer',
      ],
    • Add withValidator function, so after Laravel validate rules()`, 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'));
      }
      });
      }

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 in request. 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 & between validation, while the max of between lower than max validation (255 chars). It should not using between & max simultaneously. If using max then should using min validation.
  • The date_of_week column 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'];
}
}

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
    1. job_id
      1. required
      2. integer
      3. exists
      select *
      from `jod_jobs`
      where
      `id` = ?
      and `jod_jobs`.`deleted_at` is null
      limit 1;
    2. location_id
      1. required
      2. integer
    3. job_template_id
      1. integer
    4. job_type_id
      1. required
      2. integer
    5. job_title
      1. required
      2. string
      3. max 100 chars
      4. min 2 chars
    6. job_description
      1. required
      2. string
    7. special_instructions
      1. nullable
      2. string
    8. hourly_rate
      1. required
      2. numeric
    9. job_start_date
      1. required
      2. date, with format: Y-m-d H:i:s
      3. after or equal current time
    10. job_end_date
      1. required
      2. date, with format: Y-m-d H:i:s
      3. after job_start_date
    11. cash
      1. required
      2. 1 or 0
    12. bank_transfer
      1. required
      2. 1 or 0
    13. cheque
      1. required
      2. 1 or 0
    14. food_hygiene_certificate_required
      1. required
      2. 1 or 0
    15. date_of_week
      1. required
      2. comma separated
      3. must in : monday, tuesday, wednesday, thursday, friday, saturday, sunday
      4. will create slot array between job_start_date & job_end_date
    16. required_certificates
      1. required
      2. json

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

Backend Jod Job

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
(?, ?, ?, ?, ?, ?)

List Job

Description

List of JOD jobs

Contract

  • URL: /api/portal/jodJob/indexJobPosting
  • Method: GET
  • Permission: list-jod-jobs
  • Request:
    1. limit
      1. integer
    2. page
      1. integer
    3. location_id
      1. integer
    4. min_wage
      1. numeric
      2. if max_wage exists, this value must lower than max_wage
    5. max_wage
      1. numeric
      2. if min_wage exists, must greater than min_wage
    6. job_start_date
      1. date
      2. date_format: Y-m-d
    7. job_end_date
      1. date
      2. date_format: Y-m-d
      3. after or equal job_start_date
    8. sort_field
      1. in:
        1. created_at
        2. job_start_date
        3. job_end_date
        4. job_title
        5. status
        6. no_users_apply_job
        7. no_users_selected
        8. hourly_rate
        9. location_name
        10. applicants
    9. sort_order
      1. asc or desc
    10. filter_types
      1. string comma separated (numeric)
      2. job jod status
    11. key_word
      1. string
    12. job_with_applicants
      1. 1 or 0

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