From b2310e95d32d9c526d1be336fea7fa19958049d9 Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <angelfqc.18@gmail.com>
Date: Fri, 1 Nov 2019 13:39:03 -0500
Subject: [PATCH 1/2] Database: Add course category as foreign key to course -
 refs BT#15992

---
 src/CoreBundle/Entity/Course.php              | 18 ++++-----
 src/CoreBundle/Entity/CourseCategory.php      | 36 ++++++++++++++++++
 .../Schema/V200/Version20191101132000.php     | 37 +++++++++++++++++++
 3 files changed, 82 insertions(+), 9 deletions(-)
 create mode 100644 src/CoreBundle/Migrations/Schema/V200/Version20191101132000.php

diff --git a/src/CoreBundle/Entity/Course.php b/src/CoreBundle/Entity/Course.php
index 328635236f..f3f4457201 100644
--- a/src/CoreBundle/Entity/Course.php
+++ b/src/CoreBundle/Entity/Course.php
@@ -22,7 +22,6 @@ use Symfony\Component\Validator\Constraints as Assert;
  * @ORM\Table(
  *  name="course",
  *  indexes={
- *      @ORM\Index(name="category_code", columns={"category_code"}),
  *      @ORM\Index(name="directory", columns={"directory"}),
  *  }
  * )
@@ -205,11 +204,12 @@ class Course extends AbstractResource implements ResourceInterface
     protected $description;
 
     /**
-     * @var string
+     * @var CourseCategory
      *
-     * @ORM\Column(name="category_code", type="string", length=40, nullable=true, unique=false)
+     * @ORM\ManyToOne(targetEntity="Chamilo\CoreBundle\Entity\CourseCategory", inversedBy="courses")
+     * @ORM\JoinColumn(name="category", referencedColumnName="id")
      */
-    protected $categoryCode;
+    protected $category;
 
     /**
      * @var int
@@ -772,13 +772,13 @@ class Course extends AbstractResource implements ResourceInterface
     /**
      * Set categoryCode.
      *
-     * @param string $categoryCode
+     * @param string $category
      *
      * @return Course
      */
-    public function setCategoryCode($categoryCode)
+    public function setCategory($category)
     {
-        $this->categoryCode = $categoryCode;
+        $this->category = $category;
 
         return $this;
     }
@@ -788,9 +788,9 @@ class Course extends AbstractResource implements ResourceInterface
      *
      * @return string
      */
-    public function getCategoryCode()
+    public function getCategory()
     {
-        return $this->categoryCode;
+        return $this->category;
     }
 
     /**
diff --git a/src/CoreBundle/Entity/CourseCategory.php b/src/CoreBundle/Entity/CourseCategory.php
index 99ce8dd45c..ad483c254f 100644
--- a/src/CoreBundle/Entity/CourseCategory.php
+++ b/src/CoreBundle/Entity/CourseCategory.php
@@ -104,6 +104,13 @@ class CourseCategory
      */
     protected $urls;
 
+    /**
+     * @var ArrayCollection
+     *
+     * @ORM\OneToMany(targetEntity="Chamilo\CoreBundle\Entity\Course", mappedBy="category")
+     */
+    protected $courses;
+
     /**
      * Constructor.
      */
@@ -111,6 +118,7 @@ class CourseCategory
     {
         $this->childrenCount = 0;
         $this->children = new ArrayCollection();
+        $this->courses = new ArrayCollection();
     }
 
     /**
@@ -373,4 +381,32 @@ class CourseCategory
 
         return $this;
     }
+
+    /**
+     * @return ArrayCollection
+     */
+    public function getCourses(): ArrayCollection
+    {
+        return $this->courses;
+    }
+
+    /**
+     * @param ArrayCollection $courses
+     *
+     * @return CourseCategory
+     */
+    public function setCourses(ArrayCollection $courses): CourseCategory
+    {
+        $this->courses = $courses;
+
+        return $this;
+    }
+
+    /**
+     * @param Course $course
+     */
+    public function addCourse(Course $course)
+    {
+        $this->courses[] = $course;
+    }
 }
diff --git a/src/CoreBundle/Migrations/Schema/V200/Version20191101132000.php b/src/CoreBundle/Migrations/Schema/V200/Version20191101132000.php
new file mode 100644
index 0000000000..97d7686734
--- /dev/null
+++ b/src/CoreBundle/Migrations/Schema/V200/Version20191101132000.php
@@ -0,0 +1,37 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+namespace Chamilo\CoreBundle\Migrations\Schema\V200;
+
+use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo;
+use Doctrine\DBAL\Schema\Schema;
+
+/**
+ * Class Version20191101132000.
+ *
+ * @package Chamilo\CoreBundle\Migrations\Schema\V200
+ */
+class Version20191101132000 extends AbstractMigrationChamilo
+{
+    /**
+     * @param Schema $schema
+     */
+    public function up(Schema $schema)
+    {
+        $this->getEntityManager();
+
+        $this->addSql('ALTER TABLE course ADD category INT DEFAULT NULL');
+        $this->addSql('UPDATE course co SET co.category = (SELECT cat.id FROM course_category cat WHERE cat.code = co.category_code)');
+        $this->addSql('DROP INDEX category_code ON course');
+        $this->addSql('ALTER TABLE course DROP category_code');
+        $this->addSql('ALTER TABLE course ADD CONSTRAINT FK_169E6FB964C19C1 FOREIGN KEY (category) REFERENCES course_category (id)');
+        $this->addSql('CREATE INDEX IDX_169E6FB964C19C1 ON course (category)');
+    }
+
+    /**
+     * @param Schema $schema
+     */
+    public function down(Schema $schema)
+    {
+    }
+}

From b59c35836a943b8a3e57b50ab246944dc3aee9bb Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <angelfqc.18@gmail.com>
Date: Fri, 1 Nov 2019 14:00:12 -0500
Subject: [PATCH 2/2] Use course category ID in courses - refs BT#15992

---
 main/admin/course_add.php              | 10 +++++-----
 main/admin/course_edit.php             | 13 ++++++-------
 main/admin/course_list.php             | 18 ++++++++++--------
 main/inc/lib/add_course.lib.inc.php    | 15 ++++++++++-----
 main/inc/lib/api.lib.php               |  8 ++++----
 main/inc/lib/course.lib.php            |  2 +-
 main/inc/lib/course_category.lib.php   |  4 ++--
 src/CoreBundle/Entity/Course.php       |  2 +-
 src/CoreBundle/Framework/Container.php |  9 +++++++++
 src/GraphQlBundle/Map/MutationMap.php  |  6 +++++-
 10 files changed, 53 insertions(+), 34 deletions(-)

diff --git a/main/admin/course_add.php b/main/admin/course_add.php
index 1b43660090..9a5195a6be 100755
--- a/main/admin/course_add.php
+++ b/main/admin/course_add.php
@@ -69,7 +69,7 @@ if ($countCategories >= 100) {
 
     $form->addElement(
         'select_ajax',
-        'category_code',
+        'category_id',
         get_lang('Category'),
         null,
         ['url' => $url]
@@ -79,13 +79,13 @@ if ($countCategories >= 100) {
         $accessUrlId,
         api_get_configuration_value('allow_base_course_category')
     );
-    $categoriesOptions = [null => get_lang('none')];
+    $categoriesOptions = [0 => get_lang('None')];
     /** @var CourseCategory $category */
     foreach ($categories as $category) {
-        $categoriesOptions[$category->getCode()] = (string) $category;
+        $categoriesOptions[$category->getId()] = (string) $category;
     }
     $form->addSelect(
-        'category_code',
+        'category_id',
         get_lang('Category'),
         $categoriesOptions
     );
@@ -230,7 +230,7 @@ if ($form->validate()) {
     $course['wanted_code'] = $course['visual_code'];
     $course['gradebook_model_id'] = isset($course['gradebook_model_id']) ? $course['gradebook_model_id'] : null;
     // Fixing category code
-    $course['course_category'] = isset($course['category_code']) ? $course['category_code'] : '';
+    $course['category_id'] = isset($course['category_id']) ? (int) $course['category_id'] : '';
 
     include_once api_get_path(SYS_CODE_PATH).'lang/english/trad4all.inc.php';
     $file_to_include = api_get_path(SYS_CODE_PATH).'lang/'.$course['course_language'].'/trad4all.inc.php';
diff --git a/main/admin/course_edit.php b/main/admin/course_edit.php
index ec51c40d0f..a87f4b3da4 100755
--- a/main/admin/course_edit.php
+++ b/main/admin/course_edit.php
@@ -153,7 +153,7 @@ if ($countCategories >= 100) {
 
     $categorySelect = $form->addElement(
         'select_ajax',
-        'category_code',
+        'category_id',
         get_lang('Category'),
         null,
         ['url' => $url]
@@ -164,20 +164,19 @@ if ($countCategories >= 100) {
         $categorySelect->addOption($data['name'], $data['code']);
     }
 } else {
-    $courseInfo['category_code'] = $courseInfo['categoryCode'];
     $categories = $courseCategoriesRepo->findAllInAccessUrl(
         $urlId,
         api_get_configuration_value('allow_base_course_category')
     );
-    $categoriesOptions = [null => get_lang('none')];
+    $categoriesOptions = [0 => get_lang('None')];
 
     /** @var CourseCategory $category */
     foreach ($categories as $category) {
-        $categoriesOptions[$category->getCode()] = (string) $category;
+        $categoriesOptions[$category->getId()] = (string) $category;
     }
 
     $form->addSelect(
-        'category_code',
+        'category_id',
         get_lang('Category'),
         $categoriesOptions
     );
@@ -355,7 +354,7 @@ if ($form->validate()) {
 
     $teachers = isset($course['course_teachers']) ? $course['course_teachers'] : '';
     $title = $course['title'];
-    $category_code = isset($course['category_code']) ? $course['category_code'] : '';
+    $category_code = isset($course['category_id']) ? (int) $course['category_id'] : '';
     $department_name = $course['department_name'];
     $department_url = $course['department_url'];
     $course_language = $course['course_language'];
@@ -375,7 +374,7 @@ if ($form->validate()) {
     $params = [
         'course_language' => $course_language,
         'title' => $title,
-        'category_code' => $category_code,
+        'category' => $category_code,
         'visual_code' => $visual_code,
         'department_name' => $department_name,
         'department_url' => $department_url,
diff --git a/main/admin/course_list.php b/main/admin/course_list.php
index 8e19af1e48..9ce71fd4c3 100755
--- a/main/admin/course_list.php
+++ b/main/admin/course_list.php
@@ -94,22 +94,24 @@ function get_number_of_courses()
 function get_course_data($from, $number_of_items, $column, $direction)
 {
     $course_table = Database::get_main_table(TABLE_MAIN_COURSE);
+    $tblCourseCategory = Database::get_main_table(TABLE_MAIN_CATEGORY);
 
     $sql = "SELECT  
-                code AS col0,
+                course.code AS col0,
                 title AS col1,
-                code AS col2,
+                course.code AS col2,
                 course_language AS col3,
-                category_code AS col4,
+                category.code AS col4,
                 subscribe AS col5,
                 unsubscribe AS col6,
-                code AS col7,
+                course.code AS col7,
                 visibility AS col8,
                 directory as col9,
                 visual_code,
                 directory,
                 course.id
-    		FROM $course_table course";
+    		FROM $course_table course
+    		LEFT JOIN $tblCourseCategory category ON course.category = category.id ";
 
     if ((api_is_platform_admin() || api_is_session_admin()) &&
         api_is_multiple_url_enabled() && api_get_current_access_url_id() != -1
@@ -123,7 +125,7 @@ function get_course_data($from, $number_of_items, $column, $direction)
         $keyword = Database::escape_string("%".trim($_GET['keyword'])."%");
         $sql .= " WHERE (
             title LIKE '".$keyword."' OR
-            code LIKE '".$keyword."' OR
+            course.code LIKE '".$keyword."' OR
             visual_code LIKE '".$keyword."'
         )
         ";
@@ -139,7 +141,7 @@ function get_course_data($from, $number_of_items, $column, $direction)
         $keyword_unsubscribe = Database::escape_string($_GET['keyword_unsubscribe']);
 
         $sql .= " WHERE
-                (code LIKE '".$keyword_code."' OR visual_code LIKE '".$keyword_code."') AND
+                (course.code LIKE '".$keyword_code."' OR visual_code LIKE '".$keyword_code."') AND
                 title LIKE '".$keyword_title."' AND
                 course_language LIKE '".$keyword_language."' AND
                 visibility LIKE '".$keyword_visibility."' AND
@@ -147,7 +149,7 @@ function get_course_data($from, $number_of_items, $column, $direction)
                 unsubscribe LIKE '".$keyword_unsubscribe."'";
 
         if (!empty($keyword_category)) {
-            $sql .= " AND category_code LIKE '".$keyword_category."' ";
+            $sql .= " AND category.code LIKE '".$keyword_category."' ";
         }
     }
 
diff --git a/main/inc/lib/add_course.lib.inc.php b/main/inc/lib/add_course.lib.inc.php
index 778d618de2..d50e985725 100755
--- a/main/inc/lib/add_course.lib.inc.php
+++ b/main/inc/lib/add_course.lib.inc.php
@@ -786,7 +786,7 @@ class AddCourse
         $visual_code = $params['visual_code'];
         $directory = $params['directory'];
         $tutor_name = isset($params['tutor_name']) ? $params['tutor_name'] : null;
-        $category_code = isset($params['course_category']) ? $params['course_category'] : '';
+        $categoryId = isset($params['category_id']) ? (int) $params['category_id'] : '';
         $course_language = isset($params['course_language']) && !empty($params['course_language']) ? $params['course_language'] : api_get_setting(
             'platformLanguage'
         );
@@ -870,6 +870,8 @@ class AddCourse
         if ($ok_to_register_course) {
             $repo = Container::getCourseRepository();
             $course = new \Chamilo\CoreBundle\Entity\Course();
+            /** @var \Chamilo\CoreBundle\Entity\CourseCategory $courseCategory */
+            $courseCategory = Container::getCourseCategoryRepository()->find($categoryId);
             $urlId = 1;
             if (api_get_current_access_url_id() !== -1) {
                 $urlId = api_get_current_access_url_id();
@@ -882,7 +884,7 @@ class AddCourse
                 ->setCourseLanguage($course_language)
                 ->setTitle($title)
                 ->setDescription(get_lang('Course Description'))
-                ->setCategoryCode($category_code)
+                ->setCategory($courseCategory)
                 ->setVisibility($visibility)
                 ->setShowScore(1)
                 ->setDiskQuota($disk_quota)
@@ -984,9 +986,12 @@ class AddCourse
                             'MessageOfNewCourseToAdmin'
                         ).' '.$siteName.' - '.$iname."\n";
                     $message .= get_lang('Course name').' '.$title."\n";
-                    $message .= get_lang(
-                            'Category'
-                        ).' '.$category_code."\n";
+
+                    if ($courseCategory) {
+                        $message .= get_lang(
+                                'Category'
+                            ).' '.$courseCategory->getCode()."\n";
+                    }
                     $message .= get_lang('Coach').' '.$tutor_name."\n";
                     $message .= get_lang('Language').' '.$course_language;
 
diff --git a/main/inc/lib/api.lib.php b/main/inc/lib/api.lib.php
index 6b46436533..5f8484d0f8 100644
--- a/main/inc/lib/api.lib.php
+++ b/main/inc/lib/api.lib.php
@@ -2200,16 +2200,16 @@ function api_format_course_array(Course $course)
         return [];
     }
 
-    $categoryCode = $course->getCategoryCode();
+    $category = $course->getCategory();
 
     $courseData = [];
     $courseData['categoryCode'] = '';
     $courseData['categoryName'] = '';
-    if (!empty($categoryCode)) {
-        $categoryRepo = Database::getManager()->getRepository('ChamiloCoreBundle:CourseCategory');
-        $category = $categoryRepo->findOneBy(['code' => $categoryCode]);
+    $courseData['category_id'] = 0;
+    if ($category) {
         $courseData['categoryCode'] = $category->getCode();
         $courseData['categoryName'] = $category->getName();
+        $courseData['category_id'] = $category->getId();
     }
 
     $courseData['id'] = $courseData['real_id'] = $course->getId();
diff --git a/main/inc/lib/course.lib.php b/main/inc/lib/course.lib.php
index 016a93033b..3429a32a1c 100755
--- a/main/inc/lib/course.lib.php
+++ b/main/inc/lib/course.lib.php
@@ -2893,7 +2893,7 @@ class CourseManager
             $sql = "SELECT 
                         DISTINCT(course.code), 
                         course.id as real_id, 
-                        course.category_code AS category,
+                        course.category,
                         course.title
                     FROM $tbl_course course
                     INNER JOIN $tbl_course_user cru 
diff --git a/main/inc/lib/course_category.lib.php b/main/inc/lib/course_category.lib.php
index c4be863c68..0b85b203ac 100755
--- a/main/inc/lib/course_category.lib.php
+++ b/main/inc/lib/course_category.lib.php
@@ -96,7 +96,7 @@ class CourseCategory
                 LEFT JOIN $tbl_category t2
                 ON t1.id = t2.parent_id
                 LEFT JOIN $tbl_course t3
-                ON t3.category_code=t1.code
+                ON t3.category = t1.id
                 WHERE
                     1 = 1
                     $parentIdCondition
@@ -263,7 +263,7 @@ class CourseCategory
                 );
                 Database::query("UPDATE $tbl_category SET parent_id='".$row['parent_id']."' WHERE parent_id='$node'");
             } else {
-                Database::query("UPDATE $tbl_course SET category_code='' WHERE category_code='$node'");
+                Database::query("UPDATE $tbl_course SET category = NULL WHERE category = ".$category['id']);
                 Database::query("UPDATE $tbl_category SET parent_id=NULL WHERE parent_id='$node'");
             }
 
diff --git a/src/CoreBundle/Entity/Course.php b/src/CoreBundle/Entity/Course.php
index f3f4457201..44996d6adc 100644
--- a/src/CoreBundle/Entity/Course.php
+++ b/src/CoreBundle/Entity/Course.php
@@ -786,7 +786,7 @@ class Course extends AbstractResource implements ResourceInterface
     /**
      * Get categoryCode.
      *
-     * @return string
+     * @return CourseCategory
      */
     public function getCategory()
     {
diff --git a/src/CoreBundle/Framework/Container.php b/src/CoreBundle/Framework/Container.php
index 0a261b5712..99f9c188a0 100644
--- a/src/CoreBundle/Framework/Container.php
+++ b/src/CoreBundle/Framework/Container.php
@@ -6,6 +6,7 @@ namespace Chamilo\CoreBundle\Framework;
 use Chamilo\CoreBundle\Component\Editor\Editor;
 use Chamilo\CoreBundle\Hook\Interfaces\HookEventInterface;
 use Chamilo\CoreBundle\Repository\AccessUrlRepository;
+use Chamilo\CoreBundle\Repository\CourseCategoryRepository;
 use Chamilo\CoreBundle\Repository\CourseRepository;
 use Chamilo\CoreBundle\Repository\IllustrationRepository;
 use Chamilo\CoreBundle\ToolChain;
@@ -382,6 +383,14 @@ class Container
         return self::$container->get('Chamilo\CoreBundle\Repository\CourseRepository');
     }
 
+    /**
+     * @return CourseCategoryRepository|object|null
+     */
+    public static function getCourseCategoryRepository()
+    {
+        return self::$container->get('Chamilo\CoreBundle\Repository\CourseCategoryRepository');
+    }
+
     /**
      * @return IllustrationRepository
      */
diff --git a/src/GraphQlBundle/Map/MutationMap.php b/src/GraphQlBundle/Map/MutationMap.php
index bae03ed2a6..7458e26881 100644
--- a/src/GraphQlBundle/Map/MutationMap.php
+++ b/src/GraphQlBundle/Map/MutationMap.php
@@ -228,7 +228,11 @@ class MutationMap extends ResolverMap implements ContainerAwareInterface
         }
 
         if (isset($courseInput['categoryCode'])) {
-            $course->setCategoryCode($courseInput['categoryCode']);
+            $courseCategory = $this->em
+                ->getRepository('ChamiloCoreBundle:CourseCategory')
+                ->findOneBy(['code' => $courseInput['categoryCode']]);
+
+            $course->setCategory($courseCategory);
         }
 
         if (!empty($courseInput['visualCode'])) {