Skip to content

Commit

Permalink
Merge pull request #9 from trms/add-save-and-delete-to-models
Browse files Browse the repository at this point in the history
add save and delete to persisted models
  • Loading branch information
sethphillips authored Sep 27, 2017
2 parents a376092 + a9aa505 commit b07feed
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 14 deletions.
31 changes: 18 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,17 @@ This package should be installed with composer and requires PHP 7+
```bash
composer install trms/carousel
```
## Instructions
[Basic Useage Examples](#basic-useage-examples)
## [Examples](#basic-useage-examples)

## Servers and Requests
## [Servers & Requests](#servers-and-requests)
[The Server Instance](#api)

[Model Requests](#modelrequest)

[File Upload Requests](#fileuploadrequest)

[Bulletin Order Requests](#bulletinorderrequest)
## Models
## [Carousel Models](#models)
[Templates](#template)

[Bulletins](#bulletin)
Expand All @@ -41,6 +40,8 @@ composer install trms/carousel

[Bulletin Sorting](#bulletin-sorting)

## [Thrown Exceptions](#exceptions)

## Basic Useage Examples

### Creating a server instance
Expand Down Expand Up @@ -152,15 +153,18 @@ Bulletin order requests are used to get the Group/Bulletin order for a given Zon
|constructor|associative array|Request Object|Pass this an associative array of values to filter the request with. (ie:['ZoneID'=>'5'])|

## Models

|Method|Parameters|Returns|Description|
|------|----------|-------|-----------|
|constructor|associative array|self|Constructor for the class, properties passed to it will be used to define the Model.|
|save|none|none|This is a convenience method for saving previously persisted models. For newly instantiated models this function will throw a `CarouselModelException`. Use the API's `save` method to save new models.|
|delete|none|none|This is a convenience method for deleting previously persisted models. For newly instantiated models this function will throw a `CarouselModelException`.|
### Bulletin
`TRMS\Carousel\Models\Bulletin`

A Bulletin is a piece of content displayed in Carousel. The closest analogy would be a slide in a power point deck.
#### Methods
|Method|Parameters|Returns|Description|
|------|----------|-------|-----------|
|constructor|associative array|Zone Object|Constructor for the class, properties passed to it will be used to define the Bulletin.|
|fromTemplate (static)|Template Object|Bulletin Object|Create a new unsaved bulletin from a template.|
|resolvePartial|none|none|Resolves partial bulletins by getting them by id from the server. See the property `PartialBulletin` for more information. This function is called when trying to save partial bulletins.|
|**Relationships**|
Expand Down Expand Up @@ -224,7 +228,6 @@ A template is the starting point for a standard Bulletin and is comprised of a b
#### Methods
|Method|Parameters|Returns|Description|
|------|----------|-------|-----------|
|constructor|associative array|Zone Object|Constructor for the class, properties passed to it will be used to define the Template.|
|setBackground|Media Object|self - chainable|Sets the background Media to be used in this model|
|getBackground|none|Media Object|Gets the related background Media model|
|addBlock|Block Object|self - chainable|Add a Block relationship to the model|
Expand Down Expand Up @@ -252,7 +255,6 @@ A group is a container for Bulletins that allows for easier viewing sorting and
#### Methods
|Method|Parameters|Returns|Description|
|------|----------|-------|-----------|
|constructor|associative array|Group Object|Constructor for the class, properties passed to it will be used to define the Group.|
|setZone|Zone Object|self - chainable|Sets the Zone to be used in this model|
|getZone|none|Zone Object|Gets the related Zone model|

Expand All @@ -270,7 +272,6 @@ Media represents audio, video images and backgrounds to be used in Bulletins and
#### Methods
|Method|Parameters|Returns|Description|
|------|----------|-------|-----------|
|constructor|associative array|Media Object|Constructor for the class, properties passed to it will be used to define the Media.|
|setUser|User Object|self - chainable|Sets the User to be used in this model|
|getUser|none|User Object|Gets the related User model|
|addTag|Tag Object|self - chainable|Add a Tag relationship to the model.|
Expand All @@ -296,7 +297,6 @@ A Zone is an area of real estate on screen where bulletins are displayed. The s
#### Methods
|Method|Parameters|Returns|Description|
|------|----------|-------|-----------|
|constructor|associative array|Zone Object|Constructor for the class, properties passed to it will be used to define the Zone.|
|addTag|ZoneTag Object|self - chainable|Add a Tag relationship to the model|
|removeTag|ZoneTag Object|self - chainable|Remove a Tag relationship from the model|
#### Properties
Expand Down Expand Up @@ -326,9 +326,6 @@ Tags are metadata that can be added to the associated model. They are used for
### ZoneTag
`TRMS\Carousel\Models\ZoneTag`
#### Methods
|Method|Parameters|Returns|Description|
|------|----------|-------|-----------|
|constructor|associative array|BulletinTag Object|Constructor for the class, properties passed to it will be used to define the Tag.|
#### Properties
|Property|type|Description|
|--------|----|-----------|
Expand Down Expand Up @@ -356,3 +353,11 @@ This represents a Group and the order of its Bulletins. The order of the `Bulle
|--------|----|-----------|
|id|string|This is set by the server and not editable.|
|Bulletins|array|An ordered array of Bulletin ids. Their order represents the order of Bulletins in the Group.|


## Exceptions
You can expect the following exceptions to be thrown if things go off the rails with the server, or if you attepmpt to do things that are unsupported or not allowed.

`TRMS\Carousel\Exceptions\CarouselAPIException`
`TRMS\Carousel\Exceptions\CarouselModelException`
`TRMS\Carousel\Exceptions\CarouselRequestException`
38 changes: 38 additions & 0 deletions Tests/APITest.php
Original file line number Diff line number Diff line change
Expand Up @@ -280,4 +280,42 @@ function test_the_api_sets_the_blocks_correctly_when_saving_a_bulletin()
$this->assertEquals(2, count($bulletin->Blocks));

}

function test_the_api_gets_set_on_a_carousel_model_when_saving_a_bulletin()
{
$mockResponder = new MockResponder;
$mock = new MockHandler([
new Response(200,[],json_encode(['Blocks'=>[[],[]]])),
]);
$handler = HandlerStack::create($mock);

$server = new API();
$bulletin = new Bulletin();
$server
->addMockHandler($handler)
->connect('server','username','password')
->save($bulletin);

$this->assertInstanceOf(API::class, $bulletin->getApi());
}

function test_a_carousel_model_that_comes_from_the_server_has_the_api_object_on_it()
{
$mockResponder = new MockResponder;
$mock = new MockHandler([
new Response(200,[],$mockResponder->bulletin()),
]);
$handler = HandlerStack::create($mock);

$request = new ModelRequest(Bulletin::class,['id'=>'1']);
$server = new API();
$bulletin = $server
->addMockHandler($handler)
->connect('server','username','password')
->get($request);

$this->assertEquals('server/carouselapi/v1/bulletins/1', (string) $mock->getLastRequest()->getUri());
$this->assertEquals('GET', (string) $mock->getLastRequest()->getMethod());
$this->assertInstanceOf(API::class, $bulletin->getApi());
}
}
104 changes: 104 additions & 0 deletions Tests/CarouselModelTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

use TRMS\Carousel\Models\Bulletin;
use TRMS\Carousel\Server\API;
use TRMS\Carousel\Exceptions\CarouselModelException;

use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Exception\RequestException;

class CarouselModelTest extends PHPUnit_Framework_TestCase
{
function test_a_persisted_model_has_a_save_function_that_uses_the_api_it_was_created_with()
{
$apiMock = \Mockery::mock(API::class);
$apiMock->shouldReceive('save')
->once()
->andReturn(new Bulletin(['id'=>'1']));

$bulletin = new Bulletin(['id'=>'1'],$apiMock);

$bulletin->save();

\Mockery::close();
}

function test_calling_save_on_a_model_without_an_api_will_throw_a_carousel_model_exception()
{
$bulletin = new Bulletin(['id'=>'1']);

try{
$bulletin->save();
} catch(CarouselModelException $e){
return;
}

$this->fail('the exception was not thrown');
}

function test_calling_save_on_a_non_persisted_model_will_throw_a_carousel_model_exception()
{
$apiMock = \Mockery::mock(API::class);
$apiMock->shouldNotReceive('save');

$bulletin = new Bulletin([],$apiMock);

try{
$bulletin->save();
} catch(CarouselModelException $e){
\Mockery::close();
return;
}

$this->fail('the exception was not thrown');

}

function test_a_persisted_model_has_a_delete_function_that_uses_the_api_it_was_created_with()
{
$apiMock = \Mockery::mock(API::class);
$apiMock->shouldReceive('delete')
->once()
->andReturn(new Bulletin(['id'=>'1']));

$bulletin = new Bulletin(['id'=>'1'],$apiMock);

$bulletin->delete();

\Mockery::close();
}

function test_calling_delete_on_a_model_without_an_api_will_throw_a_carousel_model_exception()
{
$bulletin = new Bulletin(['id'=>'1']);

try{
$bulletin->delete();
} catch(CarouselModelException $e){
return;
}

$this->fail('the exception was not thrown');
}

function test_calling_delete_on_a_non_persisted_model_will_throw_a_carousel_model_exception()
{
$apiMock = \Mockery::mock(API::class);
$apiMock->shouldNotReceive('delete');

$bulletin = new Bulletin([],$apiMock);

try{
$bulletin->delete();
} catch(CarouselModelException $e){
\Mockery::close();
return;
}

$this->fail('the exception was not thrown');

}
}
16 changes: 16 additions & 0 deletions src/Models/CarouselModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,22 @@ public function setProps(Array $props)
}
}

public function save()
{
if(!$this->api || !$this->id){
throw new CarouselModelException("You must use the API's save() method to save new models");
}
$this->api->save($this);
}

public function delete()
{
if(!$this->api || !$this->id){
throw new CarouselModelException("why would you try to delete a model that has never been persisted?");
}
$this->api->delete($this);
}

public function getSaveMethod(){
if(isset($this->id)){
return "put";
Expand Down
2 changes: 2 additions & 0 deletions src/Models/SaveableInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@
interface SaveableInterface {
public function getSaveEndpoint();
public function getSaveMethod();
public function save();
public function delete();
}
2 changes: 1 addition & 1 deletion src/Server/API.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public function save(CarouselModel $model)
];
$request = new APIRequest($this->client, $this->handler, $options);
$response = $request->$method($endpoint, json_encode($model->toArray()));

$model->setApi($this);
$model->setProps($response);
return $model;
}
Expand Down

0 comments on commit b07feed

Please sign in to comment.