-
Notifications
You must be signed in to change notification settings - Fork 486
c_item_property to nodes conversion
The table c_item_property has been removed in Chamilo v2, instead we use the following Resource-related entities:
- AbstractResource
- ResourceNode
For example, in the announcements tool, we have queries like this:
Source from 1.11.x:
https://github.com/chamilo/chamilo-lms/blob/1.11.x/main/inc/lib/AnnouncementManager.php#L2056-L2101
<?php
$condition_session = api_get_session_condition(
$session_id,
true,
true,
'announcement.session_id'
);
if (api_get_group_id() == 0) {
$group_condition = '';
} else {
$group_condition = " AND (ip.to_group_id='".api_get_group_id()."' OR ip.to_group_id = 0 OR ip.to_group_id IS NULL)";
}
$sql = "SELECT
announcement.*,
ip.visibility,
ip.to_group_id,
ip.insert_user_id
FROM $tbl_announcement announcement
INNER JOIN $tbl_item_property ip
ON (announcement.c_id = ip.c_id AND announcement.id = ip.ref)
WHERE
announcement.c_id = $courseId AND
ip.c_id = $courseId AND
ip.tool = 'announcement' AND
ip.visibility <> '2'
$group_condition
$condition_session
GROUP BY ip.ref
ORDER BY display_order DESC
LIMIT 0, $maximum";
You can see the $group_condition and $condition_session variables at the end have been built before building the query, and they are relatively complex. Adding them to the query is a complex operation by itself.
The process has been streamlined in v2, but requires some previous knowledge to use efficiently. Let's analyse the process that is required to completely convert this tool to the new Resource-type structure...
Set up a class like follows, which implements the methods required by the interface. As you can see, each tool-based class should extend AbstractResource
and implement ResourceInterface
.
Extending AbstractResource
(src/CoreBundle/Entity/AbstractResource.php) will ensure some methods are readily available (addCourseLink()
, getParent()
, getResourceNode()
, getResourceName()
, etc).
Implementing ResourceInterface
will remind you to implement those basic methods: __toString()
, getResourceIdentifier()
, getResourceName()
, setResourceName()
, getResourceNode()
, setResourceNode()
. Those (except the first two) are already defined in the AbstractResource
definition, but it's good to have the definition of what methods must be covered at a minimum. This list might change over time.
<?php
namespace Chamilo\CourseBundle\Entity;
class CAnnouncement extends AbstractResource implements ResourceInterface
{
}
The CAnnouncement
class represents the entity and the actions that can be taken on an announcement object so Chamilo can work with it.
The CAnnouncementRepository
class, in contrast, represents the methods to access the announcement in a list (the database or other). So if you want to retrieve the announcement in a table full of announcements, you need a repository class for it.
<?php
namespace Chamilo\CourseBundle\Repository;
use Chamilo\CourseBundle\Entity\CAnnouncement;
use Doctrine\Persistence\ManagerRegistry;
final class CAnnouncementRepository extends ResourceRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, CAnnouncement::class);
}
}
Because we are dealing with legacy code, we need to register this repository in the container for easy use:
<?php
namespace Chamilo\CoreBundle\Framework;
class Container
{
/**
* @return CAnnouncementRepository
*/
public static function getAnnouncementRepository()
{
return self::$container->get('Chamilo\CourseBundle\Repository\CAnnouncementRepository');
}
Legacy code:
$sql = "SELECT $select
FROM $tbl_announcement announcement
INNER JOIN $tbl_item_property ip
ON (announcement.id = ip.ref AND ip.c_id = announcement.c_id)
WHERE
announcement.c_id = $courseId AND
ip.c_id = $courseId AND
ip.tool = 'announcement' AND
(
ip.to_user_id = $user_id OR
ip.to_group_id IS NULL OR
ip.to_group_id IN (0, ".implode(", ", $group_memberships).")
) AND
ip.visibility IN ('1', '0')
$condition_session
$searchCondition
ORDER BY display_order DESC";
v2 code (no c_item_property):
<?php
$repo = Container::getAnnouncementRepository();
$course = api_get_course_entity($courseId);
$session = api_get_session_entity($session_id);
$group = api_get_group_entity(api_get_group_id());
$qb = $repo->getResourcesByCourse($course, $session, $group);
$qb->select('count(resource)');
$count = $qb->getQuery()->getSingleScalarResult();
Because, getResourcesByCourse returns a QueryBuilder, we can overwrite some parameters and return the count instead of all the elements. The method getResourcesByCourse()
should handle the conditions to filter by course, session or group natively, so you don't have to set the complex conditions we saw at the beginning anymore.
For this example, we will use the CAnnouncement resource.
Before:
<?php
$params = [
'c_id' => $courseId,
'content' => $newContent,
'title' => $title,
'end_date' => $end_date,
'display_order' => $order,
'session_id' => (int) $sessionId,
];
$last_id = Database::insert($tbl_announcement, $params);
After:
<?php
$course = api_get_course_entity($courseId);
$session = api_get_session_entity($sessionId);
$announcement = new CAnnouncement();
$announcement
->setContent($newContent)
->setTitle($title)
->setEndDate(new DateTime($end_date))
->setDisplayOrder($order)
->setParent($course)
->addCourseLink($course, $session)
;
$repo = Container::getAnnouncementRepository();
$repo->create($announcement);
Any field not treated by the parent AbstractResource
class (like setEndDate() in this case) will have to be implemented manually in the resource class (CAnnouncement in this case).
<?php
$repo = Container::getAnnouncementRepository();
$announcement = $repo->find($id);
/** @var CStudentPublication $announcement */
if ($announcement) {
$announcement->setTitle('new title');
$repo->update($announcement);
}
-
Home
- Tools and sessions
- Quiz: Importing
- Releases
- Community support strategy
- Translation management
- How to report issues
- Development
- Integration