I am learning OOP. I feel I have a grasp of basic dependency injection in that as a rule of thumb, you pass an object to a method rather than the parameters to create an object. As such, never create an object within a method, but use a factory. Right?
The code below is for connecting to a REST API. However, I feel am I overcomplicating it. The $url
which is used to access the API has a number of steps in its creation before it can be used. This is what is throwing me.
I think the best approach is to create a factory RequestFactory
, call it statically from within getRecord()
and pass the $type
(GET, POST etc) $url
, $username
, and $password
to return the results of either a ServiceGetRequest
or a ServicePostRequest
.
In my code, I have stopped just before this part however and just instantiated new objects. I don't know if the single factory is the right direction, or that I should have two factories.
My thinking is with two factories one factory would create an object RequestDetails
(containing the $username, $password and $url) to pass to the other RequestFactory
rather than a bunch of parameters as with only one factory. I assume this would adhere to basic dependency injection by passing an object to a method? The down side to creating an object just to pass to a method is that the Request Details
object then shares properties of the ServiceDetails
object - $username, $password...
My final thought is that I could instead change ServiceDetails
to include a $url
property which is empty on instantiation, then when the $url has been created, rather than create new objects using factories, simply modify the existing ServiceDetails
$url
property since this is the main object which is being passed around. I thought this could make sense to reduce needless code duplication as two objects would otherwise share the same $username
and $password
. Or would this violate SoC somehow? Or would this violate dependency injection since that property would be set only after a certain method is run?
A final question, is this implementation considered RESTful? From what I have gathered, many sources seem to imply that the API itself is either RESTful or not, rather than the code used to access it. Is this true? I have tried to aim for a RESTful connection(?) by using the HTTP verbs in the cURL context - ServiceGetRequest
, ServicePostRequest
class ServiceDetails {
public $username;
public $password;
public $api_endpoint;
public function __construct( $username, $password, $api_endpoint ) {
$this->username = $username;
$this->password = $password;
$this->api_endpoint = $api_endpoint;
}
}
class API {
public $service_details;
public function __construct( ServiceDetails $service_details ) {
$this->service_details = $service_details;
}
}
class SpecificAPI extends API {
public function getRecord( $record_id ) {
$service_details = $this->service_details;
$endpoint = $service_details->api_endpoint;
$username = $service_details->username;
$password = $service_details->password;
$url = "{$endpoint}/Function?RecordID={$record_id}";
// create obj to pass - even needed?
// should be a factory?
$request = new RequestDetails( $url, $username, $password );
//should be another factory?
$return = new ServiceGetRequest( $request );
return $return->result;
}
}
class RequestDetails {
public $url;
public $username;
public $password;
public $content;
public function __construct( $url, $username, $password, $content = '' ) {
$this->url = $url;
$this->username = $username;
$this->password = $password;
$this->content = $content;
}
}
class ServiceRequest {
public $result = [ ];
public function __construct( RequestDetails $service_details ) {
$this->executeRequest( $service_details );
}
// Stub for extends
public function initialiseOptions( RequestDetails $service_details ) {
$options = array();
return $options;
}
public function executeRequest( RequestDetails $service_details ) {
$options = $this->initialiseOptions( $service_details );
$url = $service_details->url;
$ch = curl_init( $url );
curl_setopt_array( $ch, $options );
$result = curl_exec( $ch );
curl_close( $ch );
$result = json_decode( $result, true );
$this->result = $result;
}
}
class ServicePostRequest extends ServiceRequest {
public function initialiseOptions( RequestDetails $service_details ) {
$username = $service_details->username;
$password = $service_details->password;
$json = json_encode( $service_details->content );
$options = array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_USERPWD => "{$username}:{$password}",
CURLOPT_HTTPHEADER => array( "Content-type: application/json" ),
CURLOPT_POSTFIELDS => $json
);
return $options;
}
}
class ServiceGetRequest extends ServiceRequest {
public function initialiseOptions( RequestDetails $service_details ) {
$username = $service_details->username;
$password = $service_details->password;
$options = array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_USERPWD => "{$username}:{$password}",
CURLOPT_HTTPHEADER => array( "Accept: application/json" ),
CURLOPT_SSL_VERIFYPEER => false
);
return $options;
}
}
$details = new ServiceDetails(
"username",
"password",
"http://api.service.name.com"
);
$service = new SpecificAPI( $details );
$record = $service->getRecord( '007' );
print_r( $record );