We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 41406 ☆ A M B ☆
    • 30 Posts
    So I'm working on a core extension to add a consolidated API for managing and connecting to OAuth Services. The aim is to provide a base class (or classes) that can be extended (as quickly and easily as possible) to provide service-specific APIs for use in snippets/3pc's etc..

    I've hit a bit of a fork in the road about how best to provide the library so I thought I might throw it out and see what others thought of the two directions I've been deciding on. I can't see any definitive arguments to write off either approach, so any thoughts/opinions are most welcome!




    Approach #1: Service clients are extended from xPDOObject

    Plus points

    • Tighter integration with MODx/xPDO as all service clients are instances of xPDOObject
    • Data persistence is natively available from within the class parent
    • Client instances can be retrieved and initialized via xPDO::getObject()

    Drawbacks

    • Adding service class types will require creating new xPDO schemas/maps, slowing the deployment time




    Approach #2: Service clients carry xPDOObjects as properties

    Plus points

    • Lightning-fast deployment of new services (just extend class into a new file, and off you go)
    • No xPDO schema generation required
    • Transport packages only need file resolvers rather than file & db
    • Data persistence is abstracted from xPDO reliance adding some futureproofing

    Drawbacks

    • Data persistence is abstraced from xPDO, meaning existing documentation is useless
    • Low-level API is not as intuitive as data persistence is handled through an object property of the main class
    • ... can't really express the last one, just get a grubby feeling that I should be doing it the other way



    Which road would you take?
      • 8386 ☆ A M B ☆
      • 160 Posts
      I would go for approach #2, you already know how much I hate xPDO schema's. I like decoupled code, I think future proof is better than now proof. I hope to see the future of MODX using components like Composer to autoload classes to allow more abstraction away from the core and current MODX way of doing things.
        • 18373 ☆ A M B ☆
        • 3,141 Posts
        What do your service classes need xPDOObjects for?

        Quote from: easylancer at Jan 28, 2013, 04:11 PM
        I hope to see the future of MODX using components like Composer to autoload classes to allow more abstraction away from the core and current MODX way of doing things.

        Pretty sure I saw autoloading being developed somewhere...
          Mark Hamstra • Developer spending his days working on Premium Extras and a MODX Site Dashboard with the ability to remotely upgrade MODX and extras to make the MODX world a little better.

          Tweet me @mark_hamstra, check my infrequent blog at markhamstra.com, my slightly more frequent ramblings at MODX.today or see code at Github.
          • 41406 ☆ A M B ☆
          • 30 Posts
          The services use xPDOObject's for persisting OAuth Client ID/Secret (globally, per service) and also maintaining Tokens & State codes between service requests (on a per-user basis). Its a toss-up between wrapping it all neatly inside the extended xPDOObject class, and separating it out so that a new class can be written without having to prepare a schema for it.
            • 22303 MODX Staff
            • 10,725 Posts
            Is there any reason the existing remote_key and remote_data fields on the modUser class would not suffice for storing tokens and state codes between service requests on a per-user basis? Or modRegistry perhaps? Now storing "service" instances might require a persistent object class, but this should just be one xPDOObject that is used by a non-persistent class that is loaded from the model; a sort of domain controller. See modX::getService() for the standard way of loading custom classes representing your domain model. These are often referred to as service classes in MODX, though there is no semantic relation to OAuth "services" you are representing here.

            Quote from: easylancer at Jan 28, 2013, 04:11 PM
            I would go for approach #2, you already know how much I hate xPDO schema's. I like decoupled code, I think future proof is better than now proof. I hope to see the future of MODX using components like Composer to autoload classes to allow more abstraction away from the core and current MODX way of doing things.
            You know the schema is not actually used at run-time right? It's just used to generate the map file describing the class and table-specific metadata. You can always write the map files by hand... smiley

            Quote from: markh at Jan 28, 2013, 06:30 PM
            What do your service classes need xPDOObjects for?
            Quote from: easylancer at Jan 28, 2013, 04:11 PM
            I hope to see the future of MODX using components like Composer to autoload classes to allow more abstraction away from the core and current MODX way of doing things.

            Pretty sure I saw autoloading being developed somewhere...
            Indeed, I have been experimenting with various approaches to make the core framework PSR-0 compliant. Nothing written in stone yet, but I do have a couple of approaches that are partially working without too much destruction of backwards compatibility.
              • 41406 ☆ A M B ☆
              • 30 Posts
              Is there any reason the existing remote_key and remote_data fields on the modUser class would not suffice for storing tokens and state codes between service requests on a per-user basis?
              There may be several OAuth Clients for each user (twitter,facebook,github etc etc) active at one time, so can't use the modUser fields. Also, there needs to be a 'master' connection available to all user (for site-wide connections, twitter feeds etc).

              Or modRegistry perhaps?
              I don't like the idea of storing auth credentials (tokens/secrets etc) on the filesystem

              To clarify my terminology in this situation:
              oauthService - A web-based 3rd party service consisting of both authorization server and resource server [RFC 6749 section 1.1]
              modxService - A php class that can be initialized using modX::getService to provide extended core functionality

              As it stands at the moment, I have a functional modxService class to provide connectivity to one or more oauthServices. The schema for data storage is as follows (some fields omitted for brevity):
              <?xml version="1.0" encoding="UTF-8"?>
              <model package="oauth" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" version="1.1">
              
              
                  <object class="OAuthServiceType" table="oauth_clients" extends="xPDOSimpleObject">
                      <field key="name" dbtype="varchar" precision="100" phptype="string" null="false" />
                      <field key="path" dbtype="varchar" precision="255" phptype="string" null="false" default="" />
                      <field key="client_id" dbtype="varchar" precision="255" phptype="string" null="false" default="" />
                      <field key="client_secret" dbtype="varchar" precision="255" phptype="string" null="false" default="" />
                      <composite alias="Tokens" class="OAuthToken" local="id" foreign="service" cardinality="many" owner="local" />
                  </object>
              
                  <object class="OAuthToken" table="oauth_tokens" extends="xPDOSimpleObject">
                      <field key="service" dbtype="int" precision="11" attributes="unsigned" phptype="integer" null="false" />
                      <field key="user" dbtype="int" precision="11" attributes="unsigned" phptype="integer" null="false" />
                      <field key="oauth_state" dbtype="varchar" precision="255" phptype="string" null="false" />
                      <field key="token_granted" dbtype="datetime" phptype="string" null="false" />
                      <field key="oauth_token" dbtype="varchar" precision="255" phptype="string" null="false" />
                      <composite alias="User" class="modUser" local="user" foreign="id" cardinality="one" owner="foreign" />
                      <composite alias="Service" class="OAuthServiceType" local="service" foreign="id" cardinality="one" owner="foreign" />
                  </object>
              
              </model>


              To get an interface to an oauthService, a brief outline of the following code is used:

              <?php
                  $modx->getService('OAuth',$path_to_class);
              
                  /** @var string Name of xpdo/OAuthServiceType object to load */
                  $serviceName = 'GitHib';
              
                  /** @var int ID of user to initialize the connection for. 0 is master account. */
                  $modxUserId  = 0; 
              
                  /**
                   * Load an instance of a class capable of authorizing & communicating with the oauthService 
                   * @var TheClassTypeInQuestion
                   */
                  $github = $modx->OAuth->load('GitHub');
              
              
                  if($github->authorized){
                      // We have an auth token, go ahead and query the API
                      $apiResponse = $github->GET('/path/to/endpoint');
                  } else {
                      // No auth token, need to do authorization flow
                      $authUrl = $github->getAuthorizationUrl();
                      echo '<a href="'.$authUrl.'">Click here to authorize github</a>';
                  }
                  
              


              This all works fine, my question more related to the inheritance path for the api classes (dubbed TheClassTypeInQuestion above). It's only really a case of coding style i guess, but was just wondering what others thought. Each instance of the class uses an xpdo OAuthToken object for persisting it's data. This object is stored as a property of the class for access, which means that read/write on the OAuthToken needs to be abstracted in the main class.

              The other direction I was considering was to extend TheClassTypeInQuestion from XPDOObject/OAuthToken, putting all the custom functionality directly into the class. This means that an instance can be grabbed directly with modX::getObject. The downside is it requires more work to add a new oauthService interface.

              I have a feeling i'm starting to ramble on a bit, so i'll stop typing now. Hopefully someone can distill some sense of what i'm asking... If not i'll wait for the coffee to wear off and try again.
                • 41406 ☆ A M B ☆
                • 30 Posts
                Perhaps a simpler way of demonstrating the two imagined approaches...

                <?php
                
                class GitHubClient extends xPDOObject {
                   
                   public function makeAPIRequest(){
                       $token = $this->get('token');
                       $this->GET();
                   }
                }
                
                /** ====== OR ======= **/
                
                class GitHubClient {
                
                   public function makeAPIRequest(){
                       $tokenObj = $this->modx->getObject('OAuthToken');
                       $token = $tokenObj->getToken();
                       $this->GET();
                   }
                
                }
                
                  • 8386 ☆ A M B ☆
                  • 160 Posts
                  I always try to not extend xPDO classes directly, probably create a base class that extends xPDO then let your classes extend that class. That way if xPDOObject was to get deprecated or you need to switch away from it you only need to change your base class.

                  <?php
                  class BaseClassName extends xPDOObject {
                  
                  }
                  
                  
                  class GitHubClient extends BaseClassName {
                      
                     public function makeAPIRequest(){
                         $token = $this->get('token');
                         $this->GET();
                     }
                  }