Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor for SOLID compliance - Breaking on getAllIndexes (method return) and Index class (renamed into Indexes) #43

Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
0e4c4bb
wip
ppshobi Jun 18, 2020
aa036c9
HTTP contract and client
ppshobi Jun 18, 2020
5475b55
let client be an independent class
ppshobi Jun 18, 2020
57986d9
let class Index be an endpoint instance
ppshobi Jun 18, 2020
98ff789
show index test passes
ppshobi Jun 18, 2020
c43ed53
passing all testcases in IndexTest
ppshobi Jun 18, 2020
4392ba3
dedicated endpoint classes
ppshobi Jun 18, 2020
9b12832
make client tests pass with the new classes
ppshobi Jun 18, 2020
3674c77
change the exception type since the php type system will prevent us f…
ppshobi Jun 18, 2020
4937034
passes all documents test with added typehints
ppshobi Jun 18, 2020
68613bf
Keys and Permissions endpoint and test passes
ppshobi Jun 18, 2020
991c24a
change return type to mixed since few methods can return booleans as …
ppshobi Jun 18, 2020
231b1d4
move endpoints to appropriate directories
ppshobi Jun 18, 2020
9462e02
delegate index related methods to handlesIndex trait and system relat…
ppshobi Jun 18, 2020
b1e1bcf
remove dead class
ppshobi Jun 18, 2020
8fa5145
implement patch request
ppshobi Jun 18, 2020
71ce5a1
generate php doc for HTTP client class
ppshobi Jun 18, 2020
598da68
extract common methods to abstract class
ppshobi Jun 18, 2020
1e2729d
cleanup
ppshobi Jun 18, 2020
228fe40
extract related methods to appropriate traits
ppshobi Jun 18, 2020
d4c723d
lint
ppshobi Jun 18, 2020
fca3abe
Merge branch 'master' into refactor-to-have-better-SOLID-principles-c…
ppshobi Jun 19, 2020
7e9da1e
change SearchRules to SearchSettings to be synonymous with meilisearc…
ppshobi Jun 23, 2020
8ca5694
Merge branch 'refactor-to-have-better-SOLID-principles-compliance' of…
ppshobi Jun 23, 2020
9f98566
add return type
ppshobi Jun 25, 2020
bcc5c12
rename handlesSearchSettings -> handlesSettings to adhere to meilisea…
ppshobi Jun 25, 2020
3a6b7b7
Merge branch 'master' into refactor-to-have-better-SOLID-principles-c…
ppshobi Jun 25, 2020
b8658e5
Merge remote-tracking branch 'upstream/master'
ppshobi Jun 25, 2020
227bf11
Merge branch 'master' into refactor-to-have-better-SOLID-principles-c…
ppshobi Jun 25, 2020
c876e12
Merge branch 'refactor-to-have-better-SOLID-principles-compliance' of…
ppshobi Jun 25, 2020
9cf1ae3
rename trait to capital camelcasing
ppshobi Jun 26, 2020
ce28f33
added typehints and const visibility
ppshobi Jun 26, 2020
fd092fa
more typehint
ppshobi Jun 26, 2020
504ae70
more typehints
ppshobi Jun 26, 2020
748337f
more typehints and variable renameings
ppshobi Jun 26, 2020
136b770
import proper traits
ppshobi Jun 26, 2020
d4076c8
lint
ppshobi Jun 26, 2020
2a432b3
lint
ppshobi Jun 26, 2020
db9f02d
Merge remote-tracking branch 'upstream/master'
ppshobi Jul 3, 2020
176a986
Merge branch 'master' into refactor-to-have-better-SOLID-principles-c…
ppshobi Jul 3, 2020
ee1c4dc
move method inside the trait
ppshobi Jul 3, 2020
5e50cb1
use camel case
ppshobi Jul 3, 2020
1f5b92c
fix class names and rename variables inside HTTPRequestException.php …
ppshobi Jul 3, 2020
3892ef2
WIP
ppshobi Jul 3, 2020
b9ceee8
WIP
ppshobi Jul 3, 2020
9ac4495
Move HTTPRequestException test inside ClientTest since with the new i…
ppshobi Jul 3, 2020
071402d
lint
ppshobi Jul 3, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
135 changes: 52 additions & 83 deletions src/Client.php
Expand Up @@ -2,89 +2,58 @@

namespace MeiliSearch;

class Client extends HTTPRequest
use MeiliSearch\Delegates\handlesIndex;
use MeiliSearch\Delegates\handlesSystem;
ppshobi marked this conversation as resolved.
Show resolved Hide resolved
use MeiliSearch\Endpoints\Health;
use MeiliSearch\Endpoints\Indexes;
use MeiliSearch\Endpoints\Keys;
use MeiliSearch\Endpoints\Stats;
use MeiliSearch\Endpoints\SysInfo;
use MeiliSearch\Endpoints\Version;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;

class Client
{
// Indexes

public function getAllIndexes()
{
return $this->httpGet('/indexes');
}

public function showIndex($uid)
{
return $this->indexInstance($uid)->show();
}

public function deleteIndex($uid)
{
return $this->indexInstance($uid)->delete();
}

public function deleteAllIndexes()
{
foreach ($this->getAllIndexes() as $index) {
$this->deleteIndex($index['uid']);
}
}

public function getIndex($uid)
{
return $this->indexInstance($uid);
}

public function createIndex($index_uid, $options = [])
{
$body = array_merge(
$options,
['uid' => $index_uid]
);
$response = $this->httpPost('/indexes', $body);
$uid = $response['uid'];

return $this->indexInstance($uid);
}

// Health

public function health()
{
return $this->httpGet('/health');
}

// Stats

public function version()
{
return $this->httpGet('/version');
}

public function sysInfo()
{
return $this->httpGet('/sys-info');
}

public function prettySysInfo()
{
return $this->httpGet('/sys-info/pretty');
}

public function stats()
{
return $this->httpGet('/stats');
}

// Keys

public function getKeys()
{
return $this->httpGet('/keys');
}

// Private methods

private function indexInstance($uid)
{
return new Index($uid, $this->base_url, $this->api_key);
use handlesIndex;
use handlesSystem;

private $http;
/**
* @var Indexes
*/
private $index;

/**
* @var Health
*/
private $health;
/**
* @var Version
*/
private $version;
/**
* @var SysInfo
*/
private $sysInfo;
/**
* @var Keys
*/
private $keys;
/**
* @var Stats
*/
private $stats;
ppshobi marked this conversation as resolved.
Show resolved Hide resolved

public function __construct($url, $apiKey = null, ClientInterface $httpClient = null, RequestFactoryInterface $requestFactory = null, StreamFactoryInterface $streamFactory = null)
ppshobi marked this conversation as resolved.
Show resolved Hide resolved
{
$this->http = new Http\Client($url, $apiKey, $httpClient, $requestFactory, $streamFactory);
$this->index = new Indexes($this->http);
$this->health = new Health($this->http);
$this->version = new Version($this->http);
$this->sysInfo = new SysInfo($this->http);
$this->stats = new Stats($this->http);
$this->keys = new Keys($this->http);
}
}
21 changes: 21 additions & 0 deletions src/Contracts/Endpoint.php
@@ -0,0 +1,21 @@
<?php

namespace MeiliSearch\Contracts;

abstract class Endpoint
{
/**
* @var Http
*/
protected $http;

public function __construct(Http $http)
{
$this->http = $http;
}

public function show()
ppshobi marked this conversation as resolved.
Show resolved Hide resolved
{
return $this->http->get(static::PATH);
}
}
16 changes: 16 additions & 0 deletions src/Contracts/Http.php
@@ -0,0 +1,16 @@
<?php

namespace MeiliSearch\Contracts;

interface Http
{
public function get($path, array $query = []);

public function post(string $path, $body = null, array $query = []);

public function put(string $path, $body = null, array $query = []);

public function patch(string $path, $body = null, array $query = []);

public function delete($path, array $query = []);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return typehint for each method?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is that the responses can be null, array, or string. So the return type has to be mixed which I believe is same as not having a return type. Otherwise we will have to alter the response type be a DTO or something like your last comment. Which will break a lot of things in the public api. So I think the return types for these methods should be in a separate PR. with appropriate BC break labels or something, what do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, maybe it could be a good idea to submit a new PR that force more strict "logic" and types, but for now, that's okay for me 🙂

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, this kind of big change would be appreciated, but in another PR 🙂

}
41 changes: 41 additions & 0 deletions src/Delegates/handlesIndex.php
@@ -0,0 +1,41 @@
<?php

namespace MeiliSearch\Delegates;

use MeiliSearch\Endpoints\Indexes;

trait handlesIndex
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Must be uppercase on h

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I was thinking that the camelCased trait names would subtly indicate that its a trait instead of a class being used, If my memory is correct somewhere I have seen this practice (possibly laravel). So I had followed them in my projects as well.

But when I went back and checked they are also using CapitalCamelCase traits. So I have renamed all the traits to be CapitalCamelCased.

{
public function getAllIndexes()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this there no return value type on this method? I get why deleteIndex does not have a return type because MeiliSearch does not return anything, but should this one not be array too?

And also, why is handlesIndex in this delegate folder ./src/Delegates and not ./src/Delegates/Endpoints/Delegates. What is the idea behind this architecture?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this there no return value type on this method? I get why deleteIndex does not have a return type because MeiliSearch does not return anything, but should this one not be array too?

Declaring the returned type in PHP is not mandatory, but it's a good practice since it has been introduced if I'm not wrong 🙂

I don't have any answer for the second point, I let @ppshobi doing it 😇

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I have updated it with array return type.
  2. renamed trait from handlesSearchSettings to handlesSettings
  3. The handlesIndex trait is more like a proxy which transfers the calls to the actual Indexes endpoint rather than the handling the actual http requests.All these methods were on the Client.php directly, which was basically separated by comments only. So I thought of grouping them together and extracted out to a trait. Since it doesn't do any actual http calls, I am a bit hesitant putting it inside another directory named Endpoint. Also even if we do it, I believe it doesn't hurt as well.
  4. I came up with this architecture to improve readability and maintainability by embracing mainly single responsibility principle. Where I have tried to extract methods out to its own classes and grouped them to traits where possible(even though they are nasty I did this to not to break public methods). Now relationship between classes are more clearly established.
    for example
    previously HttpRequest::class class was extended by Index::class and Client::class rather it can be like Index::class can have a property of HttpRequest::class which is more clear. In my approach I have a dedicated class for making http requests and the endpoint classes have this http class which can be used to make meilisearch calls. Which I believe is more clear relationship.

{
return $this->index->all();
}

public function showIndex($uid): array
{
return (new Indexes($this->http, $uid))->show();
}

public function deleteIndex($uid)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be : void as return typehint

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

{
return (new Indexes($this->http, $uid))->delete();
}

public function deleteAllIndexes()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

{
$indexes = $this->getAllIndexes();
foreach ($indexes as $index) {
$index->delete();
}
}

public function getIndex($uid): Indexes
{
return new Indexes($this->http, $uid);
}

public function createIndex($uid, $options = []): Indexes
{
return $this->index->create($uid, $options);
}
}
38 changes: 38 additions & 0 deletions src/Delegates/handlesSystem.php
@@ -0,0 +1,38 @@
<?php

namespace MeiliSearch\Delegates;

trait handlesSystem
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uppercase on h

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

{
public function health()
{
return $this->health->show();
}

// Stats

public function version()
{
return $this->version->show();
}

public function sysInfo()
{
return $this->sysInfo->show();
}

public function prettySysInfo()
{
return $this->sysInfo->pretty();
}

public function stats()
{
return $this->stats->show();
}

public function getKeys()
{
return $this->keys->show();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return typehint for each method?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}
43 changes: 43 additions & 0 deletions src/Endpoints/Delegates/handlesDocuments.php
@@ -0,0 +1,43 @@
<?php

namespace MeiliSearch\Endpoints\Delegates;

trait handlesDocuments
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uppercase on h

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

{
// Documents

public function getDocument($document_id)
{
return $this->http->get(self::PATH.'/'.$this->uid.'/documents/'.$document_id);
}

public function getDocuments($query = [])
{
return $this->http->get(self::PATH.'/'.$this->uid.'/documents', $query);
}

public function addDocuments(array $documents, $primaryKey = null)
{
return $this->http->post(self::PATH.'/'.$this->uid.'/documents', $documents, ['primaryKey' => $primaryKey]);
}

public function updateDocuments($documents, $primary_key = null)
{
return $this->http->put(self::PATH.'/'.$this->uid.'/documents', $documents, ['primaryKey' => $primary_key]);
}

public function deleteAllDocuments()
{
return $this->http->delete(self::PATH.'/'.$this->uid.'/documents');
}

public function deleteDocument($document_id)
{
return $this->http->delete(self::PATH.'/'.$this->uid.'/documents/'.$document_id);
}

public function deleteDocuments($documents)
{
return $this->http->post(self::PATH.'/'.$this->uid.'/documents/delete-batch', $documents);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return typehint on delete|update methods?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}