DEV Community

Rahul Khan
Rahul Khan

Posted on • Updated on

Custom CRUD API in Drupal 9

We will be creating a custom API for our custom module in Drupal 9 to perform basic CRUD operations using API. We will perform the operations on our custom content type.

Step 1: Creating new folders in the Custom Module
We will create a new folder in our custom module folder (candidates) as the following structure. We will place the API codes in this folder.

Controller folder

Step 2: Creating CandidatesController.php file
In this folder, we will create a new file called 'CandidatesController.php'.

<?php
/**
* @file
* Contains \Drupal\candidates\Controller\CandidatesController.
*/
namespace Drupal\candidates\Controller;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Drupal\user\Entity\User;
use Drupal\node\Entity\Node;
use Drupal\Core\Entity;
use Drupal\taxonomy\Entity\Term;

//Controller routines for candidates routes

class CandidatesController extends ControllerBase {
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Writing the APIs for CRUD operations
Now, we will write the different APIs for adding, fetching, editing & deleting the records.
Get All Candidates Details API: get_candidate_detail
Add Candidates Details API: add_candidate_detail
Fetch Single Candidate Details after adding: fetch_candidate_detail
Edit Candidates Details API: edit_candidate_detail
Delete Candidates Details API: delete_candidate

<?php
/**
* @file
* Contains \Drupal\candidates\Controller\CandidatesController.
*/
namespace Drupal\candidates\Controller;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Drupal\user\Entity\User;
use Drupal\node\Entity\Node;
use Drupal\Core\Entity;
use Drupal\taxonomy\Entity\Term;

//Controller routines for candidates routes

class CandidatesController extends ControllerBase {
    /**
    * Get All Candidates Details API
    */
    public function get_candidate_detail(Request $request) {
        global $base_url;
        try{
            $content = $request->getContent();
            $params = json_decode($content, TRUE);
            $uid = $params['uid'];
            $user = User::load($uid);
            $all_candidates = array();

            $query_string = "SELECT node_field_data.langcode AS node_field_data_langcode, node_field_data.created AS node_field_data_created, node_field_data.nid AS nid
            FROM {node_field_data} node_field_data
            WHERE (node_field_data.status = '1') AND (node_field_data.type IN ('candidate'))
            ORDER BY node_field_data_created ASC";
            $candidate_fetch_details = \Drupal::database()->query($query_string)->fetchAll();

            foreach($candidate_fetch_details as $key => $node_id){
                $nodeid = $node_id->nid;
                $node = Node::load($nodeid);

                $candidate_details['candidate_id'] = $nodeid;
                $candidate_details['candidate_name'] = $node->get('title')->value;
                $date = date_create($node->get('field_birth_date')->value);
                $birth_date = date_format($date, "d/m/Y");
                $candidate_details['candidate_dob'] = $birth_date;
                $candidate_details['candidate_gender'] = $node->get('field_gender')->value;
                $candidate_details['candidate_mobile'] = $node->get('field_mobile')->value;
                $candidate_details['candidate_email'] = $node->get('field_email_id')->value;
                $candidate_details['candidate_city'] = $node->get('field_city')->value;
                $candidate_details['candidate_country'] = $node->get('field_country')->value;
                $candidate_details['candidate_description'] = $node->get('field_description')->value;
                array_push($all_candidates, $candidate_details);
            }
            $final_api_reponse = array(
                "status" => "OK",
                "message" => "All Candidate Details",
                "result" => $all_candidates
            );
            return new JsonResponse($final_api_reponse);
        }
        catch(Exception $exception) {
            $this->exception_error_msg($exception->getMessage());
        }
    }

    /**
    * Add Candidates Details API
    */
    public function add_candidate_detail(Request $request){
        global $base_url;
        try{
            $content = $request->getContent();
            $params = json_decode($content, TRUE);

            $uid = $params['uid'];
            $user = User::load($uid);

            $date = explode('/', $params['candidate_dob']);
            $birth_date = $date[2] . "-" . $date[1] . "-" . $date[0];

            $newCandidate = Node::create([
                'type' => 'candidate',
                'uid' => 1,
                'title' => array('value' => $params['candidate_name']),
                'field_birth_date' => array('value' => $birth_date),
                'field_gender' => array('value' => $params['candidate_gender']),
                'field_mobile' => array('value' => $params['candidate_mobile']),
                'field_email_id' => array('value' => $params['candidate_email']),
                'field_city' => array('value' => $params['candidate_city']),
                'field_country' => array('value' => $params['candidate_country']),
                'field_description' => array('value' => $params['candidate_description']),
            ]);

            // Makes sure this creates a new node
            $newCandidate->enforceIsNew();

            // Saves the node, can also be used without enforceIsNew() which will update the node if a $newCandidate->id() already exists
            $newCandidate->save();
            $nid = $newCandidate->id();
            $new_candidate_details = $this->fetch_candidate_detail($nid);
            $final_api_reponse = array(
                "status" => "OK",
                "message" => "Candidate Details Added Successfully",
                "result" => $new_candidate_details,
            );
            return new JsonResponse($final_api_reponse);
        }
        catch(Exception $exception) {
            $this->exception_error_msg($exception->getMessage());
        }
    }

    public function fetch_candidate_detail($nid){
        if(!empty($nid)){
            $node = Node::load($nid);

            $date = date_create($node->get('field_birth_date')->value);
            $birth_date = date_format($date, "d/m/Y");
            $candidate_details['candidate_name'] = $node->get('title')->value;
            $candidate_details['candidate_dob'] = $birth_date;
            $candidate_details['candidate_gender'] = $node->get('field_gender')->value;
            $candidate_details['candidate_mobile'] = $node->get('field_mobile')->value;
            $candidate_details['candidate_email'] = $node->get('field_email_id')->value;
            $candidate_details['candidate_city'] = $node->get('field_city')->value;
            $candidate_details['candidate_country'] = $node->get('field_country')->value;
            $candidate_details['candidate_description'] = $node->get('field_description')->value;

            $final_api_reponse = array(
                'candidate_detail' => $candidate_details
            );
            return $final_api_reponse;
        }
        else{
            $this->exception_error_msg("Candidate details not found.");
        }
    }

    /**
    * Edit Candidates Details API
    */
    public function edit_candidate_detail(Request $request){
        global $base_url;
        try{
            $content = $request->getContent();/* reads json input from login API callback */
            $params = json_decode($content, TRUE);

            $uid = $params['uid'];
            $user = User::load($uid);

            $nid = $params['nid'];
            $date = explode('/', $params['candidate_dob']);
            $date_of_birth = $date[2] . "-" . $date[1] . "-" . $date[0];

            if(!empty($nid)){
                $node = Node::load($nid);
                $node->set("field_birth_date", array('value' => $date_of_birth));
                $node->set("field_gender", array('value' => $params['candidate_gender']));
                $node->set("field_mobile", array('value' => $params['candidate_mobile']));
                $node->set("field_email_id", array('value' => $params['candidate_email']));
                $node->set("field_city", array('value' => $params['candidate_city']));
                $node->set("field_country", array('value' => $params['candidate_country']));
                $node->set("field_description", array('value' => $params['candidate_description']));
                $node->save();
                $final_api_reponse = array(
                    "status" => "OK",
                    "message" => "Candidate Details Updated Successfully",
                );
            }
            else{
                $final_api_reponse = array(
                    "status" => "FAIL",
                    "message" => "Candidate ID is reqired",
                );
            }
            return new JsonResponse($final_api_reponse);
        }
        catch(Exception $exception) {
            $this->exception_error_msg($exception->getMessage());
        }
    }

    /**
    * Delete Candidates Details API
    */
    public function delete_candidate(Request $request){
        global $base_url;
        try{
            $content = $request->getContent();
            $params = json_decode($content, TRUE);
            $nid = $params['nid'];
            if(!empty($nid)){
                $node = \Drupal::entityTypeManager()->getStorage('node')->load($nid);
                $node->delete();
                $final_api_reponse = array(
                    "status" => "OK",
                    "message" => "Candidate record has been deleted successfully",
                );
            }
            return new JsonResponse($final_api_reponse);
        }
        catch(Exception $exception) {
            $web_service->error_exception_msg($exception->getMessage());
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Adding the API in routing.yml
Now we will add the API paths in the candidates.routing.yml file

get_candidate_detail:
 path: 'api/get_candidate_detail'
 defaults: { _controller: '\Drupal\candidates\Controller\CandidatesController::get_candidate_detail' }
 methods: [POST]
 requirements:
  _permission: 'access content'

add_candidate_detail:
 path: 'api/add_candidate_detail'
 defaults: { _controller: '\Drupal\candidates\Controller\CandidatesController::add_candidate_detail' }
 methods: [POST]
 requirements:
   _permission: 'access content'

edit_candidate_detail:
 path: 'api/edit_candidate_detail'
 defaults: { _controller: '\Drupal\candidates\Controller\CandidatesController::edit_candidate_detail' }
 methods: [POST]
 requirements:
   _permission: 'access content'

delete_candidate :
 path: 'api/delete_candidate'
 defaults: { _controller: '\Drupal\candidates\Controller\CandidatesController::delete_candidate' }
 methods: [POST]
 requirements:
   _permission: 'access content'
Enter fullscreen mode Exit fullscreen mode

Step 5: Using the APIs
Using Postman, we can use the different APIs to add, edit, delete & fetch data from our custom content type.
We will have to set the header Content-Type as application/json & method POST.
The following params can be used to add candidate data:

{
    "candidate_name" : "Rahul Khan",
    "candidate_dob" : "10/12/1990",
    "candidate_gender" : "male",
    "candidate_mobile" : "9876543210",
    "candidate_email" : "rahul@khan.com",
    "candidate_city": "Bangalore",
    "candidate_country" : "India",
    "candidate_description" : "Hello. Test description."
}
Enter fullscreen mode Exit fullscreen mode

For editing, we can use the above params to edit data. For deleting a record, we will need to pass candidate_id (nid).

Discussion (2)

Collapse
abidkhan484 profile image
AbId KhAn

Here, some clarifications are required.
1) A content type might be made with the required fields those are shown in the POST method.
2) entityManager will be EntityTypeManager as it has been deprecated.

Collapse
rahulk1011 profile image
Rahul Khan Author

I have created a custom content type to store the data & it is mentioned in my previous post & the link is mentioned in this article above. And, thank you for pointing out about the 'entityManager' function.

dev.to/rahulk1011/custom-module-in...