-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add class to properly work with moodles course category path
- Loading branch information
1 parent
eb0a714
commit 4d860ba
Showing
2 changed files
with
327 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
<?php | ||
|
||
namespace local_adler\local; | ||
|
||
use core_course_category; | ||
use Countable; | ||
use invalid_parameter_exception; | ||
use moodle_exception; | ||
|
||
class course_category_path implements Countable { | ||
private array $path; | ||
|
||
/** | ||
* @param string|null $path the path in moodle (with spaces around the /) or UNIX format (without spaces), can be empty string or null | ||
*/ | ||
public function __construct(string|null $path) { | ||
if ($path === null || strlen($path) === 0) { | ||
$this->path = []; | ||
} else { | ||
$this->path = $this->split_and_trim_path($path); | ||
} | ||
} | ||
|
||
/** | ||
* @return string the path in moodle format (with spaces around the /) | ||
*/ | ||
public function __toString(): string { | ||
return implode(' / ', $this->path); | ||
} | ||
|
||
public function get_path(): array { | ||
return $this->path; | ||
} | ||
|
||
public function count(): int { | ||
return count($this->path); | ||
} | ||
|
||
public function exists(): bool { | ||
try { | ||
$this->get_category_id(); | ||
return true; | ||
} catch (moodle_exception $e) { | ||
return false; | ||
} | ||
} | ||
|
||
/** | ||
* @throws moodle_exception if the category already exists | ||
* @throws invalid_parameter_exception if the path is empty | ||
*/ | ||
public function create(): int { | ||
if(count($this) === 0) { | ||
throw new invalid_parameter_exception('path must not be empty'); | ||
} | ||
|
||
if($this->exists()) { | ||
throw new moodle_exception('category_already_exists', 'local_adler'); | ||
} | ||
|
||
$current_category_id = 0; // top level category | ||
$current_category_path = new course_category_path(''); // this will be used to check if the category already exists | ||
|
||
foreach ($this->get_path() as $category_path_part) { | ||
// update current category path | ||
$current_category_path->append_to_path($category_path_part); | ||
|
||
// check if category already exists | ||
if ($current_category_path->exists()) { | ||
$current_category_id = $current_category_path->get_category_id(); | ||
} else { | ||
$current_category_id = core_course_category::create([ | ||
'name' => (string)$category_path_part, | ||
'parent' => $current_category_id, | ||
'visible' => 1, | ||
])->id; | ||
} | ||
} | ||
|
||
return $current_category_id; | ||
} | ||
|
||
|
||
/** | ||
* @throws moodle_exception if the category does not exist | ||
*/ | ||
public function get_category_id(): int { | ||
$categories = core_course_category::make_categories_list(); | ||
$key = array_search((string)$this, $categories); | ||
if ($key === false) { | ||
throw new moodle_exception('category_not_found', 'local_adler'); | ||
} | ||
return $key; | ||
} | ||
|
||
/** | ||
* Append a path part to the end of the path. | ||
* @throws invalid_parameter_exception if $path_part is empty | ||
*/ | ||
public function append_to_path(string $path_part): void { | ||
if (strlen($path_part) === 0) { | ||
throw new invalid_parameter_exception('path_part must not be empty'); | ||
} | ||
$this->path = array_merge($this->path, $this->split_and_trim_path($path_part)); | ||
} | ||
|
||
private function split_and_trim_path(string $path): array { | ||
// remove preceding and trailing / | ||
$path = trim($path, ' /'); | ||
|
||
$path_parts = explode('/', $path); | ||
return array_map('trim', $path_parts); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
<?php | ||
|
||
namespace local_adler\local; | ||
|
||
global $CFG; | ||
|
||
use invalid_parameter_exception; | ||
use local_adler\lib\adler_testcase; | ||
use Mockery; | ||
use moodle_exception; | ||
|
||
require_once($CFG->dirroot . '/local/adler/tests/lib/adler_testcase.php'); | ||
|
||
class course_category_path_test extends adler_testcase { | ||
public function test_constructor_with_null() { | ||
$path = new course_category_path(null); | ||
$this->assertEquals(0, count($path)); | ||
} | ||
|
||
public function test_constructor_with_empty_string() { | ||
$path = new course_category_path(''); | ||
$this->assertEquals(0, count($path)); | ||
} | ||
|
||
public function test_constructor_with_spaces() { | ||
$path = new course_category_path('category1 / category2'); | ||
$this->assertEquals(2, count($path)); | ||
} | ||
|
||
public function test_constructor_without_spaces() { | ||
$path = new course_category_path('category1/category2'); | ||
$this->assertEquals(2, count($path)); | ||
} | ||
|
||
public function test_constructor_with_single_element() { | ||
$path = new course_category_path('category1'); | ||
$this->assertEquals(1, count($path)); | ||
} | ||
|
||
public function test_constructor_with_preceding_and_trailing_slashes() { | ||
$path = new course_category_path('/category1/category2/'); | ||
$this->assertEquals(2, count($path)); | ||
} | ||
|
||
public function test_constructor_with_spaces_around_slashes() { | ||
$path = new course_category_path(' category1 / category2 '); | ||
$this->assertEquals(2, count($path)); | ||
} | ||
|
||
public function test_to_string_method() { | ||
$path = new course_category_path('category1/category2'); | ||
$this->assertEquals('category1 / category2', $path->__toString()); | ||
} | ||
|
||
public function test_get_path_method() { | ||
$path = new course_category_path('category1/category2'); | ||
$this->assertEquals(['category1', 'category2'], $path->get_path()); | ||
} | ||
|
||
public function test_count_method() { | ||
$path = new course_category_path('category1/category2'); | ||
$this->assertEquals(2, $path->count()); | ||
} | ||
|
||
private function setup_make_categories_list_mock() { | ||
$mock = Mockery::mock('alias:core_course_category'); | ||
$mock->shouldReceive('make_categories_list')->andReturn([ | ||
1 => 'category1 / category2', | ||
2 => 'category3 / category4', | ||
]); | ||
} | ||
|
||
/** | ||
* @runInSeparateProcess | ||
*/ | ||
public function test_get_category_id_method_category_exists() { | ||
$this->setup_make_categories_list_mock(); | ||
|
||
$path = new course_category_path('category1/category2'); | ||
$this->assertEquals(1, $path->get_category_id()); | ||
} | ||
|
||
/** | ||
* @runInSeparateProcess | ||
*/ | ||
public function test_get_category_id_method_category_does_not_exists() { | ||
$this->setup_make_categories_list_mock(); | ||
|
||
$path = new course_category_path('category5/category6'); | ||
$this->expectException(moodle_exception::class); | ||
$path->get_category_id(); | ||
} | ||
|
||
/** | ||
* @runInSeparateProcess | ||
*/ | ||
public function test_exists_method_category_exists() { | ||
$this->setup_make_categories_list_mock(); | ||
|
||
$path = new course_category_path('category1/category2'); | ||
$this->assertTrue($path->exists()); | ||
} | ||
|
||
/** | ||
* @runInSeparateProcess | ||
*/ | ||
public function test_exists_method_category_does_not_exist() { | ||
$this->setup_make_categories_list_mock(); | ||
|
||
$path = new course_category_path('category5/category6'); | ||
$this->assertFalse($path->exists()); | ||
} | ||
|
||
public function test_append_to_path_with_valid_path_part(): void { | ||
$path = new course_category_path('test/path'); | ||
$path->append_to_path('new_part'); | ||
$this->assertEquals('test / path / new_part', (string)$path); | ||
} | ||
|
||
public function test_append_to_path_with_empty_path_part(): void { | ||
$this->expectException(invalid_parameter_exception::class); | ||
$path = new course_category_path('test/path'); | ||
$path->append_to_path(''); | ||
} | ||
|
||
|
||
public function test_append_to_path_with_spaces_around_path_part(): void { | ||
$path = new course_category_path('test/path'); | ||
$path->append_to_path(' new_part '); | ||
$this->assertEquals('test / path / new_part', (string)$path); | ||
} | ||
|
||
public function test_append_to_path_with_multiple_parts_in_path_part(): void { | ||
$path = new course_category_path('test/path'); | ||
$path->append_to_path('new_part1/new_part2'); | ||
$this->assertEquals('test / path / new_part1 / new_part2', (string)$path); | ||
$this->assertEquals(4, $path->count()); | ||
} | ||
|
||
public function test_count_zero_exists_false(): void { | ||
$path = new course_category_path(''); | ||
$this->assertEquals(0, $path->count()); | ||
$this->assertFalse($path->exists()); | ||
} | ||
|
||
public function test_create_with_count_zero(): void { | ||
$this->expectException(invalid_parameter_exception::class); | ||
$path = new course_category_path(''); | ||
$path->create(); | ||
} | ||
|
||
public function test_create_with_empty_path(): void { | ||
$this->expectException(invalid_parameter_exception::class); | ||
$path = new course_category_path(''); | ||
$path->create(); | ||
} | ||
|
||
/** | ||
* @runInSeparateProcess | ||
*/ | ||
public function test_create_category_already_exists(): void { | ||
$this->setup_make_categories_list_mock(); | ||
|
||
$path = new course_category_path('category1/category2'); | ||
$this->expectException(moodle_exception::class); | ||
$this->expectExceptionMessage('category_already_exists'); | ||
$path->create(); | ||
} | ||
|
||
|
||
/** | ||
* @runInSeparateProcess | ||
*/ | ||
public function test_create_with_one_segment(): void { | ||
$mock = Mockery::mock('alias:core_course_category'); | ||
$mock->shouldReceive('make_categories_list')->andReturn([]); | ||
$mock->shouldReceive('create')->andReturn((object) ['id' => 1]); | ||
|
||
$path = new course_category_path('segment1'); | ||
$this->assertEquals(1, $path->count()); | ||
$this->assertFalse($path->exists()); | ||
$this->assertIsInt($path->create()); | ||
} | ||
|
||
/** | ||
* @runInSeparateProcess | ||
*/ | ||
public function test_create_with_two_segments_first_exists(): void { | ||
$mock = Mockery::mock('alias:core_course_category'); | ||
$mock->shouldReceive('make_categories_list')->andReturn([42 => 'segment1']); | ||
$mock->shouldReceive('create')->andReturn((object) ['id' => 1]); | ||
|
||
$path = new course_category_path('segment1/segment2'); | ||
$this->assertEquals(2, $path->count()); | ||
$this->assertFalse($path->exists()); | ||
$this->assertIsInt($path->create()); | ||
} | ||
|
||
/** | ||
* @runInSeparateProcess | ||
*/ | ||
public function test_create_with_two_segments_none_exists(): void { | ||
$mock = Mockery::mock('alias:core_course_category'); | ||
$mock->shouldReceive('make_categories_list')->andReturn([]); | ||
$mock->shouldReceive('create')->andReturn((object) ['id' => 1]); | ||
|
||
$path = new course_category_path('segment1/segment2'); | ||
$this->assertEquals(2, $path->count()); | ||
$this->assertFalse($path->exists()); | ||
$this->assertIsInt($path->create()); | ||
} | ||
} |