├── .gitignore ├── tests ├── config.php.example ├── MoodleTestCase.php └── services │ └── CourseTest.php ├── phpunit.xml.example ├── src ├── Entities │ ├── SummaryFormatType.php │ ├── CourseCollection.php │ ├── Entity.php │ ├── Dto │ │ └── Course.php │ └── Course.php ├── Clients │ ├── ClientAdapterInterface.php │ ├── Adapters │ │ ├── SoapClient.php │ │ └── RestClient.php │ └── BaseAdapter.php ├── Exceptions │ └── ApiException.php ├── GenericCollection.php ├── Connection.php └── Services │ ├── Service.php │ └── Course.php ├── composer.json ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | /phpunit.xml 3 | /tests/config.php 4 | .idea -------------------------------------------------------------------------------- /tests/config.php.example: -------------------------------------------------------------------------------- 1 | [ 5 | 'url' => '', 6 | 'token' => '', 7 | ], 8 | ]; 9 | -------------------------------------------------------------------------------- /phpunit.xml.example: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | tests 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/Entities/SummaryFormatType.php: -------------------------------------------------------------------------------- 1 | items; 26 | } 27 | 28 | /** 29 | * Get iterator 30 | * @return ArrayIterator 31 | */ 32 | public function getIterator() 33 | { 34 | return new ArrayIterator($this->items); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ozq/moodle-client", 3 | "description": "Moodle client", 4 | "license": "MIT", 5 | "version": "1.0.0", 6 | "authors": [ 7 | { 8 | "name": "Sergey Volkov" 9 | } 10 | ], 11 | "minimum-stability": "dev", 12 | "prefer-stable": true, 13 | "require": { 14 | "guzzlehttp/guzzle": "5.x", 15 | "beberlei/assert": "2.7.x" 16 | }, 17 | "require-dev": { 18 | "phpunit/phpunit": "5.x" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "Ozq\\MoodleClient\\": "src/" 23 | } 24 | }, 25 | "autoload-dev": { 26 | "psr-4": { 27 | "Ozq\\MoodleClient\\Tests\\": "tests/" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/MoodleTestCase.php: -------------------------------------------------------------------------------- 1 | config = require('config.php'); 30 | $this->connection = new Connection($this->config['connection']['url'], $this->config['connection']['token']); 31 | } 32 | 33 | /** 34 | * @return array|mixed 35 | */ 36 | public function getConfig() 37 | { 38 | return $this->config; 39 | } 40 | 41 | /** 42 | * @return Connection 43 | */ 44 | public function getConnection() 45 | { 46 | return $this->connection; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Entities/CourseCollection.php: -------------------------------------------------------------------------------- 1 | items = $courses; 20 | } 21 | 22 | /** 23 | * Add course to collection 24 | * @param Course $item 25 | */ 26 | public function add(Course $item) 27 | { 28 | $this->items[$item->id] = $item; 29 | } 30 | 31 | /** 32 | * Remove course from collection 33 | * @param Course|integer $course 34 | */ 35 | public function remove($course) 36 | { 37 | $id = ($course instanceof Course) ? $course->id : $course; 38 | if (array_key_exists($id, $this->items)) { 39 | unset($this->items[$id]); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Sergey Volkov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Connection.php: -------------------------------------------------------------------------------- 1 | setUrl($url); 31 | $this->token = $token; 32 | } 33 | 34 | /** 35 | * Set URL 36 | * @param string $url 37 | */ 38 | protected function setUrl($url) 39 | { 40 | Assertion::url($url); 41 | $this->url = trim($url, '/'); 42 | } 43 | 44 | /** 45 | * Get URL 46 | * @return string 47 | */ 48 | public function getUrl() 49 | { 50 | return $this->url; 51 | } 52 | 53 | /** 54 | * Get token 55 | * @return string 56 | */ 57 | public function getToken() 58 | { 59 | return $this->token; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Clients/Adapters/SoapClient.php: -------------------------------------------------------------------------------- 1 | getClient()->__soapCall($function, $arguments); 27 | 28 | $this->handleException($response); 29 | 30 | return $response; 31 | } 32 | 33 | /** 34 | * Build client instance 35 | * @return BaseSoapClient 36 | */ 37 | protected function buildClient() 38 | { 39 | $endPoint = $this->getEndPoint([ 40 | self::OPTION_WSDL => 1, 41 | self::OPTION_TOKEN => $this->getConnection()->getToken(), 42 | ]); 43 | 44 | return new BaseSoapClient($endPoint); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Entities/Entity.php: -------------------------------------------------------------------------------- 1 | fill($data); 18 | } 19 | 20 | /** 21 | * Fill entity properties from array 22 | * @param array $data 23 | * @return $this 24 | */ 25 | public function fill(array $data) 26 | { 27 | if (!empty($data)) { 28 | $properties = $this->getProperties(); 29 | foreach ($properties as $property => $value) { 30 | $incomingProperty = strtolower($property); 31 | if (array_key_exists($incomingProperty, $data)) { 32 | $this->{$property} = $data[$incomingProperty]; 33 | } 34 | } 35 | } 36 | 37 | return $this; 38 | } 39 | 40 | /** 41 | * Get available entity properties 42 | * @return array 43 | */ 44 | public function getProperties() 45 | { 46 | return get_object_vars($this); 47 | } 48 | 49 | /** 50 | * Convert entity to array 51 | * @return array 52 | */ 53 | public function toArray() 54 | { 55 | return (array)$this; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Services/Service.php: -------------------------------------------------------------------------------- 1 | client = $client; 27 | } 28 | 29 | /** 30 | * Get service alias 31 | * @return string 32 | */ 33 | public function getAlias() 34 | { 35 | $reflectionClass = new ReflectionClass(static::class); 36 | return strtolower($reflectionClass->getShortName()); 37 | } 38 | 39 | /** 40 | * Get array of converted to arrays entities 41 | * Each entity data contains only filled entity properties 42 | * @param Entity[] ...$entities 43 | * @return array 44 | */ 45 | protected function prepareEntityForSending(Entity ...$entities) 46 | { 47 | $convertedEntities = []; 48 | 49 | foreach ($entities as $entity) { 50 | $filledData = []; 51 | $entityData = $entity->toArray(); 52 | foreach ($entityData as $property => $value) { 53 | if (!empty($value)) { 54 | $filledData[strtolower($property)] = $value; 55 | } 56 | } 57 | 58 | $convertedEntities[] = $filledData; 59 | } 60 | 61 | return $convertedEntities; 62 | } 63 | 64 | /** 65 | * Send API request 66 | * @param $function 67 | * @param array $arguments 68 | * @return array 69 | */ 70 | final protected function sendRequest($function, array $arguments = []) 71 | { 72 | $response = $this->client->sendRequest($function, $arguments); 73 | 74 | return (array)$response; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Clients/Adapters/RestClient.php: -------------------------------------------------------------------------------- 1 | setResponseFormat($responseFormat); 36 | } 37 | 38 | /** 39 | * Send API request 40 | * @param $function 41 | * @param array $arguments 42 | * @return array|bool|float|int|\SimpleXMLElement|string 43 | */ 44 | public function sendRequest($function, array $arguments = []) 45 | { 46 | $configuration = [ 47 | self::OPTION_FUNCTION => $function, 48 | self::OPTION_FORMAT => $this->responseFormat, 49 | self::OPTION_TOKEN => $this->getConnection()->getToken(), 50 | ]; 51 | 52 | $response = $this->getClient()->post(null, ['body' => array_merge($configuration, $arguments)]); 53 | $this->handleException($response); 54 | 55 | $formattedResponse = $this->responseFormat === self::RESPONSE_FORMAT_JSON ? 56 | $response->json() : 57 | $response->xml(); 58 | 59 | return $formattedResponse; 60 | } 61 | 62 | /** 63 | * Build client instance 64 | * @return HttpClient 65 | */ 66 | protected function buildClient() 67 | { 68 | return new HttpClient(['base_url' => $this->getEndPoint()]); 69 | } 70 | 71 | /** 72 | * Set response format 73 | * @param string $format 74 | */ 75 | protected function setResponseFormat($format) 76 | { 77 | Assertion::inArray($format, [self::RESPONSE_FORMAT_JSON, self::RESPONSE_FORMAT_XML]); 78 | $this->responseFormat = $format; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Services/Course.php: -------------------------------------------------------------------------------- 1 | sendRequest('core_course_get_courses', ['options' => ['ids' => $ids]]); 23 | 24 | return $this->getCourseCollection($response); 25 | } 26 | 27 | /** 28 | * Get course by field 29 | * @param $field 30 | * @param $value 31 | * @return CourseCollection 32 | */ 33 | public function getByField($field, $value) 34 | { 35 | $arguments = [ 36 | 'field' => $field, 37 | 'value' => $value, 38 | ]; 39 | 40 | $response = $this->sendRequest('core_course_get_courses_by_field', $arguments); 41 | 42 | return $this->getCourseCollection($response['courses']); 43 | } 44 | 45 | /** 46 | * Create new course 47 | * @param \Ozq\MoodleClient\Entities\Dto\Course[] ...$courses 48 | * @return CourseCollection 49 | */ 50 | public function create(CourseDto ...$courses) 51 | { 52 | $response = $this->sendRequest( 53 | 'core_course_create_courses', 54 | [ 55 | 'courses' => $this->prepareEntityForSending(...$courses) 56 | ] 57 | ); 58 | 59 | return $this->getCourseCollection($response); 60 | } 61 | 62 | /** 63 | * Delete courses by ids 64 | * @param array $ids 65 | * @return mixed 66 | */ 67 | public function delete(array $ids = []) 68 | { 69 | $response = $this->sendRequest('core_course_delete_courses', ['courseids' => $ids]); 70 | 71 | return $response; 72 | } 73 | 74 | /** 75 | * Get course collection by course array 76 | * @param array $courses 77 | * @return CourseCollection 78 | */ 79 | protected function getCourseCollection(array $courses) 80 | { 81 | $courseItems = []; 82 | foreach ($courses as $courseItem) { 83 | $courseItems[] = new CourseItem($courseItem); 84 | } 85 | 86 | return new CourseCollection(...$courseItems); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Entities/Dto/Course.php: -------------------------------------------------------------------------------- 1 | connection = $connection; 42 | $this->client = $this->buildClient(); 43 | } 44 | 45 | /** 46 | * Get client 47 | * @return mixed 48 | */ 49 | protected function getClient() 50 | { 51 | return $this->client; 52 | } 53 | 54 | /** 55 | * Get connection 56 | * @return Connection 57 | */ 58 | protected function getConnection() 59 | { 60 | return $this->connection; 61 | } 62 | 63 | /** 64 | * Get endpoint 65 | * @param array $options 66 | * @return string 67 | */ 68 | protected function getEndPoint(array $options = []) 69 | { 70 | $url = $this->connection->getUrl() . '/' . $this->getScriptPath(); 71 | 72 | return $options ? $url . '?' . http_build_query($options) : $url; 73 | } 74 | 75 | /** 76 | * Get client script path depends on protocol type 77 | * @return string 78 | */ 79 | protected function getScriptPath() 80 | { 81 | return sprintf(self::SERVER_SCRIPT_PATH_TEMPLATE, $this->getProtocolType()); 82 | } 83 | 84 | /** 85 | * Get client protocol type 86 | * @return string 87 | */ 88 | protected function getProtocolType() 89 | { 90 | return $this->recognizeClientType(); 91 | } 92 | 93 | /** 94 | * Recognize client type by client class name 95 | * @return string 96 | */ 97 | protected function recognizeClientType() 98 | { 99 | $reflectionClass = new ReflectionClass(static::class); 100 | return str_replace('client', '', strtolower($reflectionClass->getShortName())); 101 | } 102 | 103 | /** 104 | * Check if response contains exceptions 105 | * @param mixed $response 106 | * @throws ApiException 107 | */ 108 | protected function handleException($response) 109 | { 110 | //TODO: convert response to array! 111 | 112 | if (array_key_exists('exception', $response)) { 113 | throw new ApiException($response['errorcode'] . ': ' . $response['message']); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /tests/services/CourseTest.php: -------------------------------------------------------------------------------- 1 | client = new RestClient($this->getConnection()); 34 | $this->service = new Course($this->client); 35 | } 36 | 37 | public function testGetAll() 38 | { 39 | $allCourses = $this->service->getAll(); 40 | 41 | $courseDto = new CourseDto(); 42 | $properties = $courseDto->getProperties(); 43 | 44 | /** @var CourseEntity $course */ 45 | foreach ($allCourses->toArray() as $course) { 46 | foreach ($properties as $property => $value) { 47 | $courseData = $course->toArray(); 48 | $this->assertArrayHasKey($property, $courseData); 49 | } 50 | } 51 | } 52 | 53 | /** 54 | * @return CourseCollection 55 | */ 56 | public function testCreate() 57 | { 58 | $courseDto = $this->buildCourse(); 59 | $createdCourses = $this->service->create($courseDto); 60 | 61 | /** @var CourseEntity $course */ 62 | foreach ($createdCourses as $course) { 63 | $courseData = $course->toArray(); 64 | $this->assertArrayHasKey('id', $courseData); 65 | $this->assertEquals($course->shortName, $courseDto->shortName); 66 | } 67 | 68 | return $createdCourses; 69 | } 70 | 71 | /** 72 | * @param CourseCollection $courses 73 | * @depends testCreate 74 | */ 75 | public function testGetByField($courses) 76 | { 77 | $createdCourses = $courses->toArray(); 78 | 79 | /** @var CourseEntity $course */ 80 | $createdCourse = $createdCourses[0]; 81 | 82 | $allCourses = $this->service->getByField('shortname', $createdCourse->shortName); 83 | 84 | foreach ($allCourses as $course) { 85 | $this->assertEquals($createdCourse->shortName, $course->shortName); 86 | } 87 | } 88 | 89 | /** 90 | * @param CourseCollection 91 | * @depends testCreate 92 | */ 93 | public function testDelete($courses) 94 | { 95 | $ids = []; 96 | /** @var CourseEntity $course */ 97 | foreach ($courses as $course) { 98 | $ids[] = $course->id; 99 | } 100 | 101 | $this->service->delete($ids); 102 | $courses = $this->service->getAll($ids); 103 | 104 | $this->assertEquals($courses->toArray(), []); 105 | } 106 | 107 | /** 108 | * @return CourseDto 109 | */ 110 | protected function buildCourse() 111 | { 112 | $courseDto = new CourseDto(); 113 | $courseDto->shortName = 'shortName_' . uniqid(); 114 | $courseDto->fullName = 'fullName_' . uniqid(); 115 | $courseDto->categoryId = 1; 116 | 117 | return $courseDto; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MOODLE PHP CLIENT 2 | 3 | ## Installation 4 | The recommended way to install the library is through Composer: 5 | 6 | ``` 7 | $ composer require ozq/moodle-client:dev-master 8 | ``` 9 | 10 | ## Usage 11 | Create instance of connection with your moodle service: 12 | ```php 13 | $connection = new Connection('http://url-to-moodle-service.com', 'Y0uR!tOken'); 14 | ``` 15 | 16 | Create instance of one of available moodle clients, e.g. REST client: 17 | ```php 18 | $client = new RestClient($connection); 19 | ``` 20 | 21 | Now, you can use moodle services. There are available some build in ready to use moodle entities. 22 | All logic and API working encapsulated in moodle services and entities. Let's create instance of Course service. 23 | 24 | Create instance: 25 | ``` 26 | $courseService = new Course($client); 27 | ``` 28 | 29 | Get all courses: 30 | ```php 31 | $courses = $courseService->getAll(); 32 | ``` 33 | 34 | Delete courses with ids: 1, 2, 3: 35 | ```php 36 | $courses = $courseService->delete([1, 2, 3]); 37 | ``` 38 | 39 | If you have to send some specific structured data, e.g., when you create new course, it is better to use special DTO's objects: 40 | ```php 41 | $courseDto = new Course(); 42 | $courseDto->name = 'Test Course'; 43 | $courseDto->fullName = 'Test Course fullname'; 44 | ... 45 | $courseService->create($courseDto); 46 | ``` 47 | 48 | If there is no build in needed services and entities, you can create it. 49 | Services must extend Service abstract class, entities (as DTO's) must extend Entity abstract class. 50 | 51 | Also, you can use moodle client without service layer: 52 | ```php 53 | $courses = $client->sendRequest('core_course_get_courses'); 54 | ``` 55 | 56 | ## Example of Laravel integration 57 | 1. Create config file (config/moodle.php) for moodle service with content: 58 | ```php 59 | [ 63 | 'url' => 'http://url-to-moodle-service.com', 64 | 'token' => 'Y0uR!tOken', 65 | ], 66 | ]; 67 | ``` 68 | 69 | 2. Create service provider: 70 | ``` 71 | $ php artisan make:provider MoodleServiceProvider 72 | ``` 73 | Example of MoodleServiceProvider register method: 74 | ```php 75 | public function register() 76 | { 77 | $this->app->singleton(ClientAdapterInterface::class, function () { 78 | $connection = new Connection(config('moodle.connection.url'), config('moodle.connection.token')); 79 | return new RestClient($connection); 80 | }); 81 | } 82 | ``` 83 | 84 | 3. Register MoodleServiceProvider: 85 | Edit your config/app.php, add ```\App\Providers\MoodleServiceProvider::class```, to 'providers' array. 86 | 87 | 4. Clear config cache: 88 | ``` 89 | $ php artisan clear-compiled 90 | $ php artisan config:clear 91 | ``` 92 | 93 | 5. Now you can use Moodle services in your project: 94 | ```php 95 | courseService = $courseService; 119 | } 120 | 121 | /** 122 | * Display a listing of the resource. 123 | * 124 | * @return \Illuminate\Http\Response 125 | */ 126 | public function index() 127 | { 128 | $courses = $this->courseService->getAll(); 129 | return view('courses.index', ['courses' => $courses]); 130 | } 131 | } 132 | ``` --------------------------------------------------------------------------------