Skip to content

Commit

Permalink
MDL-58110 core_calendar: Add proxy for modules
Browse files Browse the repository at this point in the history
Modules associated with an event are stored in the event table
as the module's name and instance number not the actual ID of the instance
in the modules table.

So to lazy load them we need a proxy that uses the module name and instance
rather than the ID.

Part of MDL-55611 epic.
  • Loading branch information
cameorn1730 authored and Damyon Wiese committed Apr 3, 2017
1 parent 55b36b1 commit e798fa7
Show file tree
Hide file tree
Showing 8 changed files with 394 additions and 32 deletions.
15 changes: 5 additions & 10 deletions calendar/classes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -562,22 +562,17 @@ public static function get_courselink($courseid) {
/**
* Get current module cache.
*
* @param array $coursecache list of course cache
* @param array $modulecache in memory module cache
* @param string $modulename name of the module
* @param int $instance module instance number
* @return \stdClass|bool $module information
*/
public static function get_module_cached(&$coursecache, $modulename, $instance) {
$module = get_coursemodule_from_instance($modulename, $instance);

if ($module === false) {
return false;
public static function get_module_cached(&$modulecache, $modulename, $instance) {
if (!isset($modulecache[$modulename . '_' . $instance])) {
$modulecache[$modulename . '_' . $instance] = get_coursemodule_from_instance($modulename, $instance);
}

if (!self::get_course_cached($coursecache, $module->course)) {
return false;
}
return $module;
return $modulecache[$modulename . '_' . $instance];
}

/**
Expand Down
11 changes: 9 additions & 2 deletions calendar/classes/local/event/core_container.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ class core_container {
*/
protected static $coursecache = array();

/**
* @var stdClass[] An array of cached modules to use with the event factory.
*/
protected static $modulecache = array();

/**
* Initialises the dependency graph if it hasn't yet been.
*/
Expand All @@ -103,7 +108,8 @@ function() {
function() {
return false;
},
self::$coursecache
self::$coursecache,
self::$modulecache
)
);

Expand All @@ -121,7 +127,8 @@ function ($dbrow) {

return !(bool)$cm->visible;
},
self::$coursecache
self::$coursecache,
self::$modulecache
);
}

Expand Down
28 changes: 20 additions & 8 deletions calendar/classes/local/event/factories/event_abstract_factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
use core_calendar\local\event\entities\event;
use core_calendar\local\event\entities\repeat_event_collection;
use core_calendar\local\event\exceptions\invalid_callback_exception;
use core_calendar\local\event\proxies\module_std_proxy;
use core_calendar\local\event\proxies\std_proxy;
use core_calendar\local\event\value_objects\event_description;
use core_calendar\local\event\value_objects\event_times;
Expand Down Expand Up @@ -58,6 +59,11 @@ abstract class event_abstract_factory implements event_factory_interface {
*/
protected $coursecachereference;

/**
* @var array Module cache reference for use with get_module_cached.
*/
protected $modulecachereference;

/**
* @var callable Bail out check for create_instance.
*/
Expand Down Expand Up @@ -91,12 +97,14 @@ public function __construct(
callable $actioncallbackapplier,
callable $visibilitycallbackapplier,
callable $bailoutcheck,
array &$coursecachereference
array &$coursecachereference,
array &$modulecachereference
) {
$this->actioncallbackapplier = $actioncallbackapplier;
$this->visibilitycallbackapplier = $visibilitycallbackapplier;
$this->bailoutcheck = $bailoutcheck;
$this->coursecachereference = &$coursecachereference;
$this->modulecachereference = &$modulecachereference;
}

public function create_instance(\stdClass $dbrow) {
Expand Down Expand Up @@ -143,13 +151,17 @@ public function create_instance(\stdClass $dbrow) {

if ($dbrow->instance && $dbrow->modulename) {
$modulename = $dbrow->modulename;
$module = new std_proxy($dbrow->instance, function($id) use ($modulename) {
return get_coursemodule_from_instance($modulename, $id);
},
(object)[
'modname' => $modulename,
'instance' => $dbrow->instance
]);
$module = new module_std_proxy(
$dbrow->modulename,
$dbrow->instance,
function($modulename, $instance) {
return \core_calendar\api::get_module_cached(
$this->modulecachereference,
$modulename,
$instance
);
}
);
}

if ($dbrow->subscriptionid) {
Expand Down
58 changes: 58 additions & 0 deletions calendar/classes/local/event/proxies/module_std_proxy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Course module stdClass proxy.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

namespace core_calendar\local\event\proxies;

defined('MOODLE_INTERNAL') || die();

use core_calendar\local\interfaces\proxy_interface;
use core_calendar\local\event\exceptions\member_does_not_exist_exception;

/**
* Course module stdClass proxy.
*
* This implementation differs from the regular std_proxy in that it takes
* a module name and instance instead of an id to construct the proxied class.
*
* This is needed as the event table does not store the id of course modules
* instead it stores the module name and instance.
*
* @copyright 2017 Cameron Ball <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class module_std_proxy extends std_proxy implements proxy_interface {
public function __construct($modulename, $instance, callable $callback, \stdClass $base = null) {
$this->modulename = $modulename;
$this->instance = $instance;
$this->callbackargs = [$modulename, $instance];
$this->callback = $callback;
$this->base = $base = is_null($base) ? new \stdClass() : $base;
$this->base->modulename = $modulename;
$this->base->instance = $instance;
}

public function get_id() {
return $this->get_proxied_instance()->id;
}
}
14 changes: 8 additions & 6 deletions calendar/classes/local/event/proxies/std_proxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ class std_proxy implements proxy_interface {
*/
protected $callback;

/**
* @var array $callbackargs Array of arguments to pass to the callback.
*/
protected $callbackargs;

/**
* @var \stdClass $base Base class to get members from.
*/
Expand All @@ -69,6 +74,7 @@ class std_proxy implements proxy_interface {
*/
public function __construct($id, callable $callback, \stdClass $base = null) {
$this->id = $id;
$this->callbackargs = [$id];
$this->callback = $callback;
$this->base = $base;
}
Expand Down Expand Up @@ -102,11 +108,7 @@ public function set($member, $value) {
}

public function get_proxied_instance() {
if ($this->class) {
return $this->class;
} else {
$callback = $this->callback;
return $callback($this->id);
}
$callback = $this->callback;
return $this->class = $this->class ? $this->class : $callback(...$this->callbackargs);
}
}
73 changes: 68 additions & 5 deletions calendar/tests/event_factory_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,13 @@ public function test_create_instance(
$this->setAdminUser();
$event = $this->create_event();
$coursecache = [];
$modulecache = [];
$factory = new event_factory(
$actioncallbackapplier,
$visibilitycallbackapplier,
$bailoutcheck,
$coursecache
$coursecache,
$modulecache
);
$dbrow->id = $event->id;
$instance = $factory->create_instance($dbrow);
Expand Down Expand Up @@ -89,6 +91,7 @@ public function test_invalid_action_callback() {
$this->setAdminUser();
$event = $this->create_event();
$coursecache = [];
$modulecache = [];
$factory = new event_factory(
function () {
return 'hello';
Expand All @@ -99,7 +102,8 @@ function () {
function () {
return false;
},
$coursecache
$coursecache,
$modulecache
);

$factory->create_instance(
Expand Down Expand Up @@ -135,6 +139,7 @@ public function test_invalid_visibility_callback() {
$this->setAdminUser();
$event = $this->create_event();
$coursecache = [];
$modulecache = [];
$factory = new event_factory(
function ($event) {
return $event;
Expand All @@ -145,7 +150,8 @@ function () {
function () {
return false;
},
$coursecache
$coursecache,
$modulecache
);

$factory->create_instance(
Expand Down Expand Up @@ -181,6 +187,7 @@ public function test_invalid_bail_callback() {
$this->setAdminUser();
$event = $this->create_event();
$coursecache = [];
$modulecache = [];
$factory = new event_factory(
function ($event) {
return $event;
Expand All @@ -191,7 +198,8 @@ function () {
function () {
return 'asdf';
},
$coursecache
$coursecache,
$modulecache
);

$factory->create_instance(
Expand Down Expand Up @@ -226,6 +234,7 @@ public function test_course_cache() {
$course = self::getDataGenerator()->create_course();
$event = $this->create_event(['courseid' => $course->id]);
$coursecache = [];
$modulecache = [];
$factory = new event_factory(
function ($event) {
return $event;
Expand All @@ -236,7 +245,8 @@ function () {
function () {
return false;
},
$coursecache
$coursecache,
$modulecache
);

$instance = $factory->create_instance(
Expand Down Expand Up @@ -265,6 +275,59 @@ function () {
$this->assertArrayHasKey($course->id, $coursecache);
}

/**
* Test the factory's module cache.
*/
public function test_module_cache() {
$this->resetAfterTest(true);
$this->setAdminUser();
$course = self::getDataGenerator()->create_course();
$event = $this->create_event(['courseid' => $course->id]);
$plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
$assigninstance = $plugingenerator->create_instance(['course' => $course->id]);

$coursecache = [];
$modulecache = [];
$factory = new event_factory(
function ($event) {
return $event;
},
function () {
return true;
},
function () {
return false;
},
$coursecache,
$modulecache
);

$instance = $factory->create_instance(
(object)[
'id' => $event->id,
'name' => 'test',
'description' => 'Test description',
'format' => 2,
'courseid' => $course->id,
'groupid' => 1,
'userid' => 1,
'repeatid' => 1,
'modulename' => 'assign',
'instance' => $assigninstance->id,
'eventtype' => 'due',
'timestart' => 123456789,
'timeduration' => 12,
'timemodified' => 123456789,
'timesort' => 123456789,
'visible' => 1,
'subscriptionid' => 1
]
);

$instance->get_course_module()->get('course');
$this->assertArrayHasKey('assign' . '_' . $assigninstance->id, $modulecache);
}

/**
* Testcases for the create instance test.
*
Expand Down
Loading

0 comments on commit e798fa7

Please sign in to comment.