Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 22
CRAP
0.00% covered (danger)
0.00%
0 / 474
ProgramService
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 22
6806.00
0.00% covered (danger)
0.00%
0 / 474
 __construct
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 2
 saveProgram
0.00% covered (danger)
0.00%
0 / 1
56.00
0.00% covered (danger)
0.00%
0 / 25
 validateSaveProgramRequest
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 6
 insertProgram
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 19
 updateProgram
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 27
 deleteProgram
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 18
 restoreProgram
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 18
 searchProgram
0.00% covered (danger)
0.00%
0 / 1
462.00
0.00% covered (danger)
0.00%
0 / 92
 getProgramDetails
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 34
 fetchProgramDetails
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 32
 validateSaveDepartmentProgramRequest
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 2
 saveDepartmentProgram
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 26
 deleteDepartmentProgram
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 15
 deleteProgramPermanently
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 12
 searchProgramWithBatchId
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 15
 validateBeforeDelete
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 16
 updateProgramName
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 21
 haveId
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 3
 getProgramByPatternDepartmentDetails
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 13
 getCurrentProgramNameByStudentId
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 13
 getAllPrograms
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 9
 getStudentPrograms
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 56
<?php
namespace com\linways\core\ams\professional\service\academic;
use com\linways\academics\core\constants\GroupTypeConstant;
use com\linways\base\util\SecurityUtils;
use com\linways\base\util\MakeSingletonTrait;
use com\linways\core\ams\professional\constant\academic\GroupTypeConstant as AcademicGroupTypeConstant;
use com\linways\core\ams\professional\service\BaseService;
use com\linways\core\ams\professional\exception\ProfessionalException;
use com\linways\core\ams\professional\mapper\academic\ProgramServiceMapper;
use com\linways\core\ams\professional\request\academic\SearchProgramRequest;
use com\linways\core\ams\professional\constant\academic\StatusConstants;
use com\linways\core\ams\professional\dto\api\Program;
class ProgramService extends BaseService
{
    use MakeSingletonTrait;
    private $mapper  = null;
    private function __construct() {
        $this->mapper = ProgramServiceMapper::getInstance()->getMapper();
    }
    /**
     * Save program
     * @param Program $program
     * @return String $id
     */
    public function saveProgram (Program $program)
    {
        // $program = $this->realEscapeObject($program);
        $program->createdBy = $GLOBALS['userId'] ?? $program->createdBy;
        $program->updatedBy = $GLOBALS['userId'] ?? $program->updatedBy;
        try{
            $this->validateSaveProgramRequest($program);
            
            if(!empty($program->id))
            {
                $program->id = $this->updateProgram($program);
            }
            else
            {
                $program->id = $this->insertProgram($program);
            }
            $this->saveDepartmentProgram($program);
        }catch(\Exception $e) {
            if($e->getCode() !== ProfessionalException::INVALID_PARAMETER && $e->getCode() !== ProfessionalException::EMPTY_PARAMETERS && $e->getCode() !== ProfessionalException::DUPLICATE_ENTRY) {
                throw new ProfessionalException($e->getCode(),"Failed to save Program! Please try again");
            } else if ($e->getCode() === ProfessionalException::DUPLICATE_ENTRY) {
                throw new ProfessionalException (ProfessionalException::DUPLICATE_ENTRY,"Cannot create program.$program->name already exists!");
            } else {
                throw new ProfessionalException ($e->getCode(),$e->getMessage());
            }
        }
        
        return $program->id;
    }
    
    /**
     * Validate Program Request Before Saving
     * @param Program $program
     * @return NULL
     */
    private function validateSaveProgramRequest(Program $program)
    {
        if(empty($program->courseTypeId))
            throw new ProfessionalException(ProfessionalException::EMPTY_PARAMETERS,"Program course type is empty! Please enter a type for program");
        if(empty($program->degreeId))
            throw new ProfessionalException(ProfessionalException::EMPTY_PARAMETERS,"Program degree is empty! Please choose degree for program");
    }
    
    /**
     * Insert Program
     * @param Program $program
     * @return String $id
     */
    private function insertProgram(Program $program)
    {
        $properties = !empty($program->properties) ? "'" . addslashes(json_encode($program->properties)) . "'" : "{}";
        if(is_array($program->streamId)){
            $streamIds = "'" . json_encode($program->streamId) . "'";
        }else{
            $streamIds = empty($program->streamId)?'"["'.$program->streamId.'"]"':"'[]'";
        }
        $id = SecurityUtils::getRandomString();
        $query = "INSERT INTO `program`
                  (`id`,`name`,`course_type_id`,`degree_id`,`stream_id`,`specialization`,`properties`,`created_by`,`updated_by`,`internal_setting`)
                  VALUES
                  ('$id','$program->name','$program->courseTypeId','$program->degreeId',$streamIds,'$program->specialization',$properties,'$program->createdBy','$program->updatedBy','$program->internal_setting')";
        
        try {
            $this->executeQuery($query);
            return $id;
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(),$e->getMessage());
        }
    }
    /**
     * Update Program
     * @param Program $program
     * @return NULL
     */
    private function updateProgram(Program $program)
    {
        $properties = !empty($program->properties) ? "'".addslashes(json_encode($program->properties))."'" : "{}";
        if(is_array($program->streamId)){
            $program->streamId = json_encode($program->streamId);
        }else{
            $program->streamId = empty($program->streamId)?'["'.$program->streamId.'"]':"[]";
        }
        $query = "UPDATE
                    `program`
                SET
                    `course_type_id` = '$program->courseTypeId',
                    `name` = '$program->name',
                    `degree_id` = '$program->degreeId',
                    `stream_id` = '$program->streamId',
                    `specialization` = '$program->specialization',
                    `properties` = $properties,
                    `updated_by` = '$program->updatedBy',
                    `internal_setting` = '$program->internal_setting'
                WHERE
                    `id` = '$program->id'";
        try {
            $this->executeQuery($query);
            return $program->id;
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(),$e->getMessage());
        }
    }
    /**
     * Delete Program (Soft Delete)
     * @param String $id
     * @return NULL
     */
    public function deleteProgram($id)
    {
        $id = $this->realEscapeString($id);
        $updatedBy = $GLOBALS['userId'];
        if(empty($id))
            throw new ProfessionalException(ProfessionalException::EMPTY_PARAMETERS,"Program is invalid! Please enter a valid program");
        $query = "UPDATE
                    `program`
                SET
                    `trashed` = UTC_TIMESTAMP(),
                    `updated_by` = '$updatedBy'
                WHERE
                    `id` = '$id'";
        try {
            $this->executeQuery($query);
        } catch (\Exception $e) {
            throw new ProfessionalException(ProfessionalException::ERROR_DELETING,"Error deleting program! Please try again");
        }
    }
    /**
     * Restore Program
     * @param String $id
     * @return NULL
     */
    public function restoreProgram($id)
    {
        $id = $this->realEscapeString($id);
        $updatedBy = $GLOBALS['userId'];
        if(empty($id))
            throw new ProfessionalException(ProfessionalException::EMPTY_PARAMETERS,"Program is invalid! Please enter a valid program");
        $query = "UPDATE
                    `program`
                SET
                    `trashed` = NULL,
                    `updated_by` = '$updatedBy'
                WHERE
                    `id` = '$id'";
        try {
            $this->executeQuery($query);
        } catch (\Exception $e) {
            throw new ProfessionalException(ProfessionalException::ERROR_RESTORING,"Error restoring academic term! Please try again");
        }
    }
    
    /**
     * Search Program Details
     * @param SearchProgramRequest $request
     * @return Program
     */
    public function searchProgram(SearchProgramRequest $request)
    {
        $request = $this->realEscapeObject($request);
        $whereQuery = "";
        $limitQuery = "";
        $grouping = "";
        $programServiceMapper = ProgramServiceMapper::SEARCH_PROGRAMS;
        if(!empty($request->id)) {
            $whereQuery .= " AND p.id='$request->id";
        }
        if($request->trashed === StatusConstants::ACTIVE) {
            $whereQuery .= " AND p.trashed IS NULL ";
        }
        if($request->trashed === StatusConstants::TRASHED) {
            $whereQuery .= " AND p.trashed IS NOT NULL ";
        }
        if($request->departmentId){
            $whereQuery .= " AND pdr.department_id='$request->departmentId";
        }
        if($request->schoolId){
            $whereQuery .= " AND dept.school_id='$request->schoolId";
    
        }
        if (!empty($request->courseTypeIds) && count($request->courseTypeIds) > 0) {
            $whereQuery .= " AND p.course_type_id IN (" . implode(",", $request->courseTypeIds) . ") ";
        }
       
        if (!empty($request->degreeIds) && count($request->degreeIds) > 0) {
            
            $result = "'" . implode ( "', '", $request->degreeIds ) . "'";
            $whereQuery .= " AND p.degree_id IN ($result";
        }
        if (!empty($request->streamIds) && count($request->streamIds) > 0) {
            $whereQuery .= " AND JSON_SEARCH(p.stream_id,'one', '".implode("') IS NOT NULL AND JSON_SEARCH(p.stream_id,'one','",$request->streamIds)."')  IS NOT NULL ";
        }
        
        if(!empty($request->type)) {
            $whereQuery .= " AND  p.specialization LIKE '%$request->type%' ";
        }
        if (!empty($request->departmentIds) && count($request->departmentIds) > 0){
            $whereQuery .= " AND  pdr.department_id IN (  " . implode(",", $request->departmentIds) . ") ";
        }
        if(!empty($request->ids)) {
            $whereQuery .= " AND p.id IN ('".(implode("','",$request->ids))."')";
        }
        if($request->startIndex !== "" && $request->endIndex !== "")
        {
            $limitQuery .= " LIMIT $request->startIndex,$request->endIndex";
        }
        $grouping .= " GROUP BY p.id ";
        if($request->includeDepartment) {
            $programServiceMapper = ProgramServiceMapper::SEARCH_DEPARTMENT_PROGRAMS;
            $grouping .= " ,pdr.id ";
        }
        if($request->filter){
            
            $orderBy = " ORDER BY p.name ";
        }else{
            $orderBy = " ORDER BY p.created_date DESC ";
        }
        $query = "SELECT
            p.id,
            p.name,
            p.course_type_id,
            ct.typeName,
            p.degree_id,
            d.name AS `degreeName`,
            p.stream_id,
            s.name AS `streamName`,
            p.specialization,
            p.properties,
            p.trashed,
            p.created_by,
            p.created_date,
            p.updated_by,
            p.updated_date,
            p.internal_setting,
            concat('[',group_concat(distinct `department_id`),']') as `departmentIds`,
            pdr.department_id, 
            pdr.properties AS `deptRelationProperties`, 
            dept.deptName, 
            dept.departmentDesc,
            vs.name as schoolName
        FROM `program` p
        INNER JOIN `course_type` ct ON ct.courseTypeID = p.course_type_id
        INNER JOIN `degree` d ON d.id = p.degree_id
        INNER JOIN `program_department_relation` pdr ON pdr.program_id = p.id 
        INNER JOIN `department` dept ON dept.deptID = pdr.department_id
        LEFT JOIN `stream` s ON JSON_SEARCH( p.stream_id, 'one', s.id) IS NOT NULL
        LEFT JOIN v4_school vs on dept.school_id = vs.id
        WHERE 1 = 1 ";
        try {
             $programs = $this->executeQueryForList($query.$whereQuery.$grouping.$orderBy.$limitQuery, $this->mapper[$programServiceMapper]);
        } catch (\Exception $e) {
            throw new ProfessionalException(ProfessionalException::ERROR_FETCHING,"Cannot fetch program details! Please try again.");
        }
        return $programs;
    } 
    /**
     * get Program by id
     * @param String $id
     * @return Program
     */
    public function getProgramDetails($id)
    {
        $id = $this->realEscapeObject($id);
        if(empty($id)) 
            throw new ProfessionalException(ProfessionalException::EMPTY_PARAMETERS,"Program is invalid! Please enter a valid program");
            $query = "SELECT
             p.id,
                p.course_type_id,
                p.name,
                ct.typeName,
                p.degree_id,
                d.name AS `degreeName`,
                p.stream_id,
                s.name AS `streamName`,
                p.specialization,
                p.properties,
                p.trashed,
                p.created_by,
                p.created_date,
                p.updated_by,
                p.updated_date
            FROM `program` p
            INNER JOIN `course_type` ct ON ct.courseTypeID = p.course_type_id
            INNER JOIN `degree` d ON d.id = p.degree_id
            INNER JOIN `program_department_relation` pdr ON pdr.program_id = p.id
            LEFT JOIN `stream` s ON JSON_SEARCH( p.stream_id, 'one', s.id) IS NOT NULL
            WHERE
                p.id = '$id'";
        try {
            $program = $this->executeQueryForObject($query,false, $this->mapper[ProgramServiceMapper::SEARCH_PROGRAMS]);
        } catch (\Exception $e) {
            throw new ProfessionalException(ProfessionalException::ERROR_FETCHING,"Cannot fetch program details! Please try again");
        }
        return $program;
    }
     /**
     * get Program by id
     * @param String $id
     * @return Program
     */
    public function fetchProgramDetails($id)
    {
        $id = $this->realEscapeObject($id);
            $query = "SELECT
             p.id,
                p.course_type_id,
                p.name,
                ct.typeName,
                p.degree_id,
                d.name AS `degreeName`,
                p.stream_id,
                s.name AS `streamName`,
                p.specialization,
                p.properties,
                p.trashed,
                p.created_by,
                p.created_date,
                p.updated_by,
                p.updated_date
            FROM `program` p
            INNER JOIN `course_type` ct ON ct.courseTypeID = p.course_type_id
            INNER JOIN `degree` d ON d.id = p.degree_id
            INNER JOIN `program_department_relation` pdr ON pdr.program_id = p.id
            LEFT JOIN `stream` s ON JSON_SEARCH( p.stream_id, 'one', s.id) IS NOT NULL
            WHERE
                p.id = '$id'";
        try {
            $program = $this->executeQueryForObject($query,false, $this->mapper[ProgramServiceMapper::SEARCH_PROGRAMS]);
        } catch (\Exception $e) {
            throw new ProfessionalException(ProfessionalException::ERROR_FETCHING,"Cannot fetch program details! Please try again");
        }
        return $program;
    }
    /**
     * Validate Department Program Request Before Saving
     * @param Program $program
     * @return NULL
     */
    private function validateSaveDepartmentProgramRequest(Program $program)
    {
        // if(empty($program->departmentIds))
        //     throw new ProfessionalException(ProfessionalException::EMPTY_PARAMETERS,"Department is empty! Please choose a department for program");
    }
    
    /**
     * save Department Program
     * @param Program $program
     * @return String $id
     */
    public function saveDepartmentProgram(Program $program)
    {
        $program = $this->realEscapeObject($program);
        $program->createdBy = $GLOBALS['userId'] ?? $program->createdBy;
        $program->updatedBy = $GLOBALS['userId'] ?? $program->updatedBy;
        $values = [];
        $sql = "DELETE FROM `program_department_relation` WHERE `program_id` = '$program->id' AND `department_id` NOT IN ( ".implode(',',$program->departmentIds)." )";
        $this->executeQuery($sql);
        foreach ($program->departmentIds as $deptId) {
            $values[] = "('$program->id','$deptId','$program->createdBy','$program->updatedBy')";
        }
        if (empty($values)) {
            return false;
        }
        $valueString = implode(",", $values);
        $query = "INSERT INTO `program_department_relation`
                  (`program_id`,`department_id`,`created_by`,`updated_by`)
                  VALUES $valueString
                  ON DUPLICATE KEY UPDATE
                  `updated_by` = VALUES(`created_by`)";
        
        try {
            $id = $this->executeQuery($query,true)->id;
            return $id;
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(),$e->getMessage());
        }
    }
    /**
     * Delete Department Program (Soft Delete)
     * @param String $id
     * @return NULL
     */
    public function deleteDepartmentProgram( $programId)
    {
        $programId = $this->realEscapeString($programId);
        $updatedBy = $GLOBALS['userId'];
        if(empty($programId))
            throw new ProfessionalException(ProfessionalException::EMPTY_PARAMETERS,"Program is invalid! Please enter a valid program");
       
        $query = "DELETE FROM
                    `program_department_relation`
                WHERE
                    `program_id` = '$programId'";
        try {
            $this->executeQuery($query);
        } catch (\Exception $e) {
            throw new ProfessionalException(ProfessionalException::ERROR_DELETING,"Error deleting program! Please try again");
        }
    }
    /**
     * Delete Program (Permanent Delete)
     * @param String $id
     * @return NULL
     */
    public function deleteProgramPermanently($programId)
    {
        $programId = $this->realEscapeString($programId);
        if(empty($programId))
            throw new ProfessionalException(ProfessionalException::EMPTY_PARAMETERS,"Program is invalid! Please enter a valid program");
       
        $this->validateBeforeDelete($programId);
        $query = "DELETE FROM `program` WHERE `id` = '$programId'";
        try {
            $this->executeQuery($query);
        } catch (\Exception $e) {
            throw new ProfessionalException(ProfessionalException::ERROR_DELETING,"Error deleting program! Please try again");
        }
    }
    /**
     * Search Program Details
     * @param SearchProgramRequest $request
     * @return programDetails
     */
    public function searchProgramWithBatchId(SearchProgramRequest $request){
        $sql = "SELECT
                    p.id,
                    p.name,
                    p.properties
                from
                    batches b
                inner join `groups` g on
                    b.groups_id = g.id
                    and g.`type` = 'BATCH'
                inner join program p on
                    g.program_id = p.id
                where
                    b.batchID = ".$request->batchId;
        return $this->executeQueryForObject($sql);
    }
    /**
     * validate before deleting a program (Permanent Delete)
     * @param String $id
     * @return NULL
     */
    public function validateBeforeDelete($id)
    {
        $id = $this->realEscapeString($id);
        if(empty($id))
            throw new ProfessionalException(ProfessionalException::EMPTY_PARAMETERS,"Program is invalid! Please enter a valid program");
            
        $request = new \stdClass;
        $request->type = AcademicGroupTypeConstant::BATCH;
        $request->properties->programId = $id;
        $batches = GroupService::getInstance()->findGroupByProperties($request);
        if(count($batches)){
            throw new ProfessionalException(ProfessionalException::HAVE_RELATIONS,"Have relation with batch(s), Can not delete the program!");
        }
        $curriculum = $this->executeQueryForObject("SELECT id FROM cm_curriculum WHERE properties->'$.programId' = '$id");
        if(!empty($curriculum)){
            throw new ProfessionalException(ProfessionalException::HAVE_RELATIONS,"Curriculum is assigned to a batch! Please enter a valid program");
        }
    }
    /**
     * Update Program Name
     * @param $programId $programName
     * @return NULL
     */
    public function updateProgramName($programId,$programName,$internalSetting,$properties)
    {
        $properties = !empty($properties) ? "'" . json_encode($properties) . "'" : "{}";
        $programId = $this->realEscapeString($programId);
        $programName = $this->realEscapeString($programName);
        $internalSetting = $this->realEscapeString($internalSetting);
        $this->haveId($programId);
        $query = "UPDATE
                    `program`
                SET
                    `name` = '$programName',
                    `internal_setting` = '$internalSetting',
                    `properties` = $properties
                WHERE
                    `id` = '$programId'";
        try {
            $this->executeQuery($query);
            return $programId;
        } catch (\Exception $e) {
            throw new ProfessionalException($e->getCode(),$e->getMessage());
        }
    }
    public function haveId($id,$error = "Program is invalid! Please enter a valid program"){
        if(empty($id))
            throw new ProfessionalException(ProfessionalException::EMPTY_PARAMETERS,$error);
    }
    public function getProgramByPatternDepartmentDetails($patterncourseId)
    {
        $patterncourseId = $this->realEscapeString($patterncourseId);
        $this->haveId($patterncourseId,"Pattern department course is invalid! Please enter a valid Pattern department course details");
        $query = "SELECT program_id FROM pattern_deptcourses pd WHERE pd.program_id = '$patterncourseId'";
        try {
            $programId = $this->executeQueryForObject($query)->program_id;
        } catch (\Exception $e) {
            throw new ProfessionalException(ProfessionalException::FAILED_TO_FETCH,"Failed to get program by pattern department course");
        }
        $programRequest = new SearchProgramRequest;
        $programRequest->id = $programId;
        return $this->searchProgram($programRequest)[0];
    }
    /**
     * @author Ajay
     * @return String
     * @param studentId
     * 
     */
    public function getCurrentProgramNameByStudentId($studentId)
    {
        $studentId = $this->realEscapeString($studentId);
        try{
            $sql = "SELECT p.name as programName, p.properties  from student_program_account spa  
            inner join `groups` bg on bg.id = spa.current_batch_id 
            inner join program p on p.id = bg.program_id 
            where spa.student_id =".$studentId;
            return $this->executeQueryForObject($sql);
        }
        catch (\Throwable $th) {
            throw new ProfessionalException(ProfessionalException::FAILED_TO_FETCH,"Error fetching student program details");
        }
    }
     /**
     * get All Programs
     * @param String $id
     * @return Program
     */
    public function getAllPrograms()
    {        
            $query = "SELECT DISTINCT name,id,stream_id,properties from program";
        try {
            $program = $this->executeQueryForList($query);
        } catch (\Exception $e) {
            throw new ProfessionalException(ProfessionalException::ERROR_FETCHING,"Cannot fetch program details! Please try again");
        }
        return $program;
    }
    public function getStudentPrograms($studentId)
    {
        $studentId = $this->realEscapeString($studentId);
        try{
            $sql = "SELECT
                cap.name as paperName
                FROM
                    `groups` g
                INNER JOIN student_program_account spa ON
                    spa.current_batch_id = g.id
                INNER JOIN department d on 
                    d.deptID = g.properties->>'$.departmentId' 
                INNER JOIN studentaccount s ON
                    s.studentID = spa.student_id
                INNER JOIN group_members gm ON
                    gm.student_id = spa.id
                    AND gm.academic_status != 'REMOVED'
                INNER JOIN `groups` sg ON
                    sg.id = gm.groups_id
                INNER JOIN groups_relations gr ON
                    gr.child_groups_id = sg.id
                    AND gr.parent_groups_id = g.id
                INNER JOIN cluster_groups_relations cgr ON 
                    cgr.groups_id = gr.child_groups_id
                INNER JOIN cluster c ON 
                    c.id = cgr.cluster_id 
                INNER JOIN cm_academic_paper_subjects caps ON
                    caps.id = sg.paperSubjectId
                INNER JOIN cm_academic_paper cap ON
                    cap.id = caps.cm_academic_paper_id
                INNER JOIN cm_syllabus_academic_term_settings csats ON
                    csats.id = cap.cm_syllabus_academic_term_settings_id
                INNER JOIN cm_syllabus cs ON
                    cs.id = csats.cm_syllabus_id
                INNER JOIN v4_ams_subject vas ON
                    vas.id = caps.ams_subject_id 
                LEFT JOIN cm_curriculum_syllabus_relation ccsr ON 
                    ccsr.cm_syllabus_id = cs.id
                LEFT JOIN cm_common_list_object cclo ON
                    cclo.id = caps.slot_id
                WHERE
                    1 = 1  AND cs.type IN ('MINOR')  AND sg.academic_term_id = g.academic_term_id  AND ccsr.id IS NOT NULL  
                    AND s.studentID = '$studentId'
                GROUP BY
                    spa.id,
                    caps.id
                ORDER BY
                    g.name,
                    s.rollNo,
                    cclo.name;";
            $studentMinor= $this->executeQueryForList($sql);
            return $studentMinor;
        }
        catch (\Throwable $th) {
            throw new ProfessionalException(ProfessionalException::ERROR_FETCHING,"Cannot fetch program details! Please try again");
        }
    }
}