CristalTeam/php-api-wrapper

Multiple API Sources (Laravel)

kylewallace opened this issue · 8 comments

Hello, I’ve just started messing around with this package and was wondering if you could point me in the right direction.

I am working in Laravel and using the Laravel “stack”.

My test project draws data from three individual API sources (totally different URL). What would be the best practice to attach each endpoint to the three individual associated models?

I’ve followed the guide but it seems to relate to using one single API source.

Good question, you're right, but it'is a documentation mistake.

If you'are following the guide about the Wrapper (https://github.com/CristalTeam/php-api-wrapper/blob/master/docs/more-about-wrapper.md) you've now a CustomWrapper class for each API (or endpoint). So, at this point, you just need to bind CustomWrapper instead of Api in your provider :

$this->app->bind(CustomWrapper::class, function(){
    $transport = new Basic(
        'username', 
        'password', 
        'http://api.example.com/v1/', 
        $this->app->make(Curl::class)
    );
    return new CustomWrapper($transport);
});

We keep the issue open to fix the document.

When you try to bind differents wrapper to differents entities, the last binding instruction override the first one.
Eg :
provider :

public function register()
    {
        $this->app->bind(VideoWrapper::class, function(){
            $transport = new Transport(
                'https://xxx.tv/',
                $this->app->make(Curl::class)
            );
            return new VideoWrapper($transport);
        });
        $this->app->bind(StreamWrapper::class, function(){
            $transport = new Bearer(
                '',
                'https://xxx.tv/',
                $this->app->make(Curl::class)
            );
            //dd($transport);
            return new StreamWrapper($transport);
        });
    }


public function boot()
    {
        // API models

        Video::setApi($this->app->make(VideoWrapper::class));
        Stream::setApi($this->app->make(StreamWrapper::class));

    }

so for example getVideo will not be accessible in VideoWrapper ...
There is something strange with binding...

I'm not realy sure to understand your problem :

  • What is the type of the object when you do dd(app(VideoWrapper::class)); ?
  • What is the real error thrown when you try a dd(app(VideoWrapper::class)->getVideo()); ?

J'ai deux wrappers que je bind comme ceci dans le provider

 public function boot()
    {
        // API models
        Stream::setApi($this->app->make(StreamWrapper::class));
        Video::setApi($this->app->make(VideoWrapper::class));
    }

Lorsque je fais

Video::find(1234) 

je récupère bien un objet Video complet

App\Models\Api\Video {#208
  #entity: "video"
  #primaryKey: "id"
  #casts: []
  +exists: true
  +wasRecentlyCreated: false
  #attributes: array:22 [
    "id" => "89d1adf3-9b49-4282-a89d-041c9e3f17a4"
    "type" => "ASSET"
    "title" => "10 Cloverfield Lane"
    "shortSynopsis" => "After a car crash, a woman wakes up inside a sealed bunker with two men. Confused and frightened, she is told a devastating chemical attack has made the air outside deadly. But as she fears for her life inside, she must prepare for what awaits outside."
    "longSynopsis" => "After a car crash, a woman wakes up inside a sealed bunker with two men. Confused and frightened, she is told a devastating chemical attack has made the air outside deadly. But as she fears for her life inside, she must prepare for what awaits outside."
    "posterImage" => ""
    "boxcoverImage" => ""
    "adult" => false
    "minimumAge" => 12
    "parentalRating" => []
    "duration" => 5940
    "credits" => array:4 [
      0 => array:2 [
        "role" => "ACTOR"
        "name" => "John Gallagher Jr."
      ]
      1 => array:2 [
        "role" => "ACTOR"
        "name" => "John Goodman"
      ]
      2 => array:2 [
        "role" => "ACTOR"
        "name" => "Mary Elizabeth Winstead"
      ]
      3 => array:2 [
        "role" => "DIRECTOR"
        "name" => "Dan Trachtenberg"
      ]
    ]
    "productionYear" => 2016
    "productionLocations" => array:1 [
      0 => "US"
    ]
    "tags" => array:2 [
      0 => "movies"
      1 => "drama"
    ]
    "genres" => array:1 [
      0 => "3.4"
    ]
    "streams" => array:1 [
      0 => array:3 [
        "id" => "a68a97bc-af3a-4528-969f-c61b322754da"
        "type" => "FULL"
        "resolution" => "SD"
      ]
    ]
    "validFrom" => "2017-12-15T23:00:00Z"
    "validUntil" => "2020-12-31T23:59:00Z"
    "score" => 0
    "operatorScore" => 0
    "catalogs" => []
  ]
  #original: array:22 [
    "id" => "89d1adf3-9b49-4282-a89d-041c9e3f17a4"
    "type" => "ASSET"
    "title" => "10 Cloverfield Lane"
    "shortSynopsis" => "After a car crash, a woman wakes up inside a sealed bunker with two men. Confused and frightened, she is told a devastating chemical attack has made the air outside deadly. But as she fears for her life inside, she must prepare for what awaits outside."
    "longSynopsis" => "After a car crash, a woman wakes up inside a sealed bunker with two men. Confused and frightened, she is told a devastating chemical attack has made the air outside deadly. But as she fears for her life inside, she must prepare for what awaits outside."
    "posterImage" => ""
    "boxcoverImage" => ""
    "adult" => false
    "minimumAge" => 12
    "parentalRating" => []
    "duration" => 5940
    "credits" => array:4 [
      0 => array:2 [
        "role" => "ACTOR"
        "name" => "John Gallagher Jr."
      ]
      1 => array:2 [
        "role" => "ACTOR"
        "name" => "John Goodman"
      ]
      2 => array:2 [
        "role" => "ACTOR"
        "name" => "Mary Elizabeth Winstead"
      ]
      3 => array:2 [
        "role" => "DIRECTOR"
        "name" => "Dan Trachtenberg"
      ]
    ]
    "productionYear" => 2016
    "productionLocations" => array:1 [
      0 => "US"
    ]
    "tags" => array:2 [
      0 => "movies"
      1 => "drama"
    ]
    "genres" => array:1 [
      0 => "3.4"
    ]
    "streams" => array:1 [
      0 => array:3 [
        "id" => "a68a97bc-af3a-4528-969f-c61b322754da"
        "type" => "FULL"
        "resolution" => "SD"
      ]
    ]
    "validFrom" => "2017-12-15T23:00:00Z"
    "validUntil" => "2020-12-31T23:59:00Z"
    "score" => 0
    "operatorScore" => 0
    "catalogs" => []
  ]
  #changes: []
  #appends: []
  #dates: []
  #dateFormat: "Y-m-d H:i:s"
  #relations: []
  #hidden: []
  #visible: []
}

par contre si j'inverse les bind dans le provider

Video::setApi($this->app->make(VideoWrapper::class));
        Stream::setApi($this->app->make(StreamWrapper::class));

et que je refais :

Video::find(1234)

l'objet est vide :

App\Models\Api\Video {#202
  #entity: "video"
  #primaryKey: "id"
  #casts: []
  +exists: true
  +wasRecentlyCreated: false
  #attributes: []
  #original: []
  #changes: []
  #appends: []
  #dates: []
  #dateFormat: "Y-m-d H:i:s"
  #relations: []
  #hidden: []
  #visible: []

et donc inversement pour Stream.
Si j'ajoute ette fonction dans le StreamWrapper :

 public function getVideo($id)
    {
        return $this->transport->request('/api/vod/v1/vod/' . $id);
    }

ça fonctionne... mais cette fonction devrait être dans le StreamWrapper.
Ce qui me fait dire que le dernier bind dans le provider prend le dessus sur les précédent.
Est-ce que je fais quelque chose d'une mauvaise manière ?
si jefais ceci :

$video = new Video();
$stream = new Stream();
        dd($video->getApi(), $stream->getApi());

j'obtiens ceci :

App\Models\Api\Wrappers\StreamWrapper {#159
  #transport: Cristal\ApiWrapper\Transports\Bearer {#149
    #token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIyIiwianRpIjoiZjE1OTliZDc3YTVmNmJiMjBhM2FkODk1ODhhNDRkMzM1NGU4NTQ5YTFmMTI0NzY0ZDBmZTE5NGUyZjEyMDAyYmMyZmQ4OTNhYjE0NWUyYjciLCJpYXQiOjE1OTg1MTU5MTAsIm5iZiI6MTU5ODUxNTkxMCwiZXhwIjoxNTk4NTIzMTEwLCJzdWIiOiIxYzc3ZmNlZTgzMTA0MGJkYjlhMTQ3MjhiNDFhY2EyNyIsInNjb3BlcyI6W10sI6k5peguoUJJtghrmlLX8hd0cn3jdVGXxB8Z2-VTECUNeVG1LhBobubn5-h5aMF263m09BwoCZ_KLxa6jMPxgSZ0xB9DLVTL8fUw7dhX1U9Nj9LoRNNQs1Dt3HYFf8PsKq-fqCoyNYyYeV6bAsaLB7biKwXPZ75I0Zj5C8eFciOViYu4hJabhFwibuKVQUfDSIeoFK5eu5_oriKL4CYI5DpmFMGqzq0XBcRtrYEG0QJQGrWyCUAp23de9L7B-6pl1V9H6nS9-_TO_pB2cn0V5tNMdMgqNTmsHaUrgJIT2M839Ymv0WHOKwYSC6E"
    #entrypoint: "https://androme.tv/"
    #client: Curl\Curl {#152
      +curl: curl resource @232
        url: ""
        content_type: null
        http_code: 0
        header_size: 0
        request_size: 0
        filetime: -1
        ssl_verify_result: 0
        redirect_count: 0
        total_time: 0.0
        namelookup_time: 0.0
        connect_time: 0.0
        pretransfer_time: 0.0
        size_upload: 0.0
        size_download: 0.0
        speed_download: 0.0
        speed_upload: 0.0
        download_content_length: -1.0
        upload_content_length: -1.0
        starttransfer_time: 0.0
        redirect_time: 0.0
        redirect_url: ""
        primary_ip: ""
        certinfo: []
        primary_port: 0
        local_ip: ""
        local_port: 0
        http_version: 0
        protocol: 0
        ssl_verifyresult: 0
        scheme: ""
        appconnect_time_us: 0
        connect_time_us: 0
        namelookup_time_us: 0
        pretransfer_time_us: 0
        redirect_time_us: 0
        starttransfer_time_us: 0
        total_time_us: 0
      }
      +id: "5f4d0cafede588.53869627"
      +error: false
      +errorCode: 0
      +errorMessage: null
      +curlError: false
      +curlErrorCode: 0
      +curlErrorMessage: null
      +httpError: false
      +httpStatusCode: 0
      +httpErrorMessage: null
      +url: ""
      +requestHeaders: null
      +responseHeaders: null
      +rawResponseHeaders: ""
      +responseCookies: []
      +response: null
      +rawResponse: null
      +beforeSendCallback: null
      +downloadCompleteCallback: null
      +successCallback: null
      +errorCallback: null
      +completeCallback: null
      +fileHandle: null
      +downloadFileName: null
      +attempts: 0
      +retries: 0
      +childOfMultiCurl: false
      +remainingRetries: 0
      +retryDecider: null
      +jsonDecoder: null
      +xmlDecoder: null
      -cookies: []
      -headers: Curl\CaseInsensitiveArray {#155
        -data: array:1 [
          "authorization" => "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIyIiwianRpIjoiZjE1OTliZDc3YTVmNmJiMjBhM2FkODk1ODhhNDRkMzM1NGU4NTQ5YTFmMTI0NzY0ZDBmZTE5NGUyZjEyMDAyYmMyZmQ4OTNhYjE0NWUyYjciLCJpYXQiOjE1OTg1MTU5MTAsIm5iZiI6MTU5ODUxNTkxMCwiZXhwIjoxNTk4NTIzMTEwLCJzdWIiOiIxYzc3ZmNlZTgzMTA0MGJkYjlhMTQ3MjhiNDFhY2EyNyIsInNjb3BlcyI6W10sImRldmljZV9pZCI6IjA0ODVmM2VjLTFkZGYtMTFlYS1iYmQzLTAwMGMyOTY4MTM5YSIsInBsYXRmb3JtIjoiV0VCIn0.NlsqP_ieVIB9_4v6k5peguoUJJtghrmlLX8hd0cn3jdVGXxB8Z2-VTECUNeVG1LhBobubn5-h5aMF263m09BwoCZ_KLxa6jMPxgSZ0xB9DLVTL8fUw7dhX1U9Nj9LoRNNQs1Dt3HYFf8PsKq-fqCoyNYyYeV6bAsaLB7biKwXPZ75I0Zj5C8eFciOViYu4hJabhFwibuKVQUfDSIeoFK5eu5_oriKL4CYI5DpmFMGqzq0XBcRtrYEG0QJQGrWyCUAp23de9L7B-6pl1V9H6nS9-_TO_pB2cn0V5tNMdMgqNTmsHaUrgJIT2M839Ymv0WHOKwYSC6E"
        ]
        -keys: array:1 [
          "authorization" => "Authorization"
        ]
      }
      -options: array:7 [
        10018 => "PHP-Curl-Class/8.8.0 (+https://github.com/php-curl-class/php-curl-class) PHP/7.4.4 curl/7.64.0"
        13 => 30
        2 => true
        20079 => Closure($ch, $header) {#154
          use: {
            $header_callback_data: {#153
              +"rawResponseHeaders": ""
              +"responseCookies": []
            }
          }
        }
        19913 => true
        10002 => ""
        10023 => array:1 [
          0 => "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIyIiwianRpIjoiZjE1OTliZDc3YTVmNmJiMjBhM2FkODk1ODhhNDRkMzM1NGU4NTQ5YTFmMTI0NzY0ZDBmZTE5NGUyZjEyMDAyYmMyZmQ4OTNhYjE0NWUyYjciLCJpYXQiOjE1OTg1MTU5MTAsIm5iZiI6MTU5ODUxNTkxMCwiZXhwIjoxNTk4NTIzMTEwLCJzdWIiOiIxYzc3ZmNlZTgzMTA0MGJkYjlhMTQ3MjhiNDFhY2EyNyIsInNjb3BlcyI5peguoUJJtghrmlLX8hd0cn3jdVGXxB8Z2-VTECUNeVG1LhBobubn5-h5aMF263m09BwoCZ_KLxa6jMPxgSZ0xB9DLVTL8fUw7dhX1U9Nj9LoRNNQs1Dt3HYFf8PsKq-fqCoyNYyYeV6bAsaLB7biKwXPZ75I0Zj5C8eFciOViYu4hJabhFwibuKVQUfDSIeoFK5eu5_oriKL4CYI5DpmFMGqzq0XBcRtrYEG0QJQGrWyCUAp23de9L7B-6pl1V9H6nS9-_TO_pB2cn0V5tNMdMgqNTmsHaUrgJIT2M839Ymv0WHOKwYSC6E"
        ]
      ]
      -jsonDecoderArgs: []
      -jsonPattern: "/^(?:application|text)\/(?:[a-z]+(?:[\.-][0-9a-z]+){0,}[\+\.]|x-)?json(?:-[a-z]+)?/i"
      -xmlDecoderArgs: []
      -xmlPattern: "~^(?:text/|application/(?:atom\+|rss\+|soap\+)?)xml~i"
      -defaultDecoder: null
      +"headerCallbackData": {#153}
    }
    #errorHandlers: array:6 [
      0 => Cristal\ApiWrapper\Exceptions\Handlers\NetworkErrorHandler {#150
        +tries: 0
        #transport: Cristal\ApiWrapper\Transports\Bearer {#149}
        #maxTries: 3
      }
      401 => Cristal\ApiWrapper\Exceptions\Handlers\UnauthorizedErrorHandler {#151
        +tries: 0
        #transport: Cristal\ApiWrapper\Transports\Bearer {#149}
        #maxTries: 3
      }
      403 => Cristal\ApiWrapper\Exceptions\Handlers\ForbiddenErrorHandler {#148
        +tries: 0
        #transport: Cristal\ApiWrapper\Transports\Bearer {#149}
        #maxTries: 3
      }
      404 => Cristal\ApiWrapper\Exceptions\Handlers\NotFoundErrorHandler {#156
        +tries: 0
        #transport: Cristal\ApiWrapper\Transports\Bearer {#149}
        #maxTries: 3
      }
      400 => Cristal\ApiWrapper\Exceptions\Handlers\BadRequestErrorHandler {#157
        +tries: 0
        #transport: Cristal\ApiWrapper\Transports\Bearer {#149}
        #maxTries: 3
      }
      422 => Cristal\ApiWrapper\Exceptions\Handlers\BadRequestErrorHandler {#158
        +tries: 0
        #transport: Cristal\ApiWrapper\Transports\Bearer {#149}
        #maxTries: 3
      }
    ]
  }
}
 App\Models\Api\Wrappers\StreamWrapper {#159
  #transport: Cristal\ApiWrapper\Transports\Bearer {#149
    #token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIyIiwianRpIjoiZjE1OTliZDc3YTVmNmJiMjBhM2FkODk1ODhhNDRkMzM1NGU4NTQ5YTFmMTI0NzY0ZDBmZTE5NGUyZjEyMDAyYmMyZmQ4OTNhYjE0NWUyYjciLCJpYXQiOjE1OTg1MTU5MTAsIm5iZiI6MTU5ODUxNTkxMCwiZXhwIjoxNTk4NTIzMTEwLCJzdWIiOiIxYzc3ZmNlZTgzMTA0MGJkYjlhMTQ3MjhiNDFhY2EyNyIsInNjb3BlcyI6W10sImRldmljZV9pZCI6IjA0ODVmM2VjLTFkZGYtMTFlYS1iYmQzLTAwMGMyOTY4MTM5YSIsInBsYXRmb3JtIjoiV0VCIn0.NlXzENREFrsAmasKlSd0_AdGN3py-khCjj-IqAXDAxWAwOX87keV8tCWd1FXi09V_YZHe5qcvJ19fse3-NbrK2lqMWiOViYu4hJabhFwibuKVQUfDSIeoFK5eu5_oriKL4CYI5DpmFMGqzq0XBcRtrYEG0QJQGrWyCUAp23de9L7B-6pl1V9H6nS9-_TO_pB2cn0V5tNMdMgqNTmsHaUrgJIT2M839Ymv0WHOKwYSC6E"
    #entrypoint: "https://androme.tv/"
    #client: Curl\Curl {#152
      +curl: curl resource @232
        url: ""
        content_type: null
        http_code: 0
        header_size: 0
        request_size: 0
        filetime: -1
        ssl_verify_result: 0
        redirect_count: 0
        total_time: 0.0
        namelookup_time: 0.0
        connect_time: 0.0
        pretransfer_time: 0.0
        size_upload: 0.0
        size_download: 0.0
        speed_download: 0.0
        speed_upload: 0.0
        download_content_length: -1.0
        upload_content_length: -1.0
        starttransfer_time: 0.0
        redirect_time: 0.0
        redirect_url: ""
        primary_ip: ""
        certinfo: []
        primary_port: 0
        local_ip: ""
        local_port: 0
        http_version: 0
        protocol: 0
        ssl_verifyresult: 0
        scheme: ""
        appconnect_time_us: 0
        connect_time_us: 0
        namelookup_time_us: 0
        pretransfer_time_us: 0
        redirect_time_us: 0
        starttransfer_time_us: 0
        total_time_us: 0
      }
      +id: "5f4d0cafede588.53869627"
      +error: false
      +errorCode: 0
      +errorMessage: null
      +curlError: false
      +curlErrorCode: 0
      +curlErrorMessage: null
      +httpError: false
      +httpStatusCode: 0
      +httpErrorMessage: null
      +url: ""
      +requestHeaders: null
      +responseHeaders: null
      +rawResponseHeaders: ""
      +responseCookies: []
      +response: null
      +rawResponse: null
      +beforeSendCallback: null
      +downloadCompleteCallback: null
      +successCallback: null
      +errorCallback: null
      +completeCallback: null
      +fileHandle: null
      +downloadFileName: null
      +attempts: 0
      +retries: 0
      +childOfMultiCurl: false
      +remainingRetries: 0
      +retryDecider: null
      +jsonDecoder: null
      +xmlDecoder: null
      -cookies: []
      -headers: Curl\CaseInsensitiveArray {#155
        -data: array:1 [
          "authorization" => "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIyIiwianRpIjoiZjE1OTliZDc3YTVmNmJiMjBhM2FkODk1ODhhNDRkMzM1NGU4NTQ5YTFmMTI0NzY0ZDBmZTE5NGUyZjEyMDAyYmMyZmQ4OTNhYjE0NWUyYjciLCJpYXQiOjE1OTg1MTU5MTAsIm5iZiI6MTU5ODUxNTkxMCwiZXhwIjoxNTk4NTIzMTEwLCJzdWIiOiIxYzc3ZmNlZTgzMTA0MGJkYjlhMTQ3MjhiNDFhY2EyNyIsInNjb3BlcyI6W10sImRldmljZV9pZCI6IjA0ODVmM2VjLTFkZGYtMTFlYS1iYmQzLTAwMGMyOTY4MTM5YSIsInBsYXRmb3JtIjoiV0VCIn0.NlsqP_ieVIBw13A-5IfIyUisnJSt2b6059tgaWFW0hMxmt26BTtniX9B8ep9pYxrmu-XzENREFrsAmasKlSd0_AdGN3py-khCjj-IqAXDAxWAwOX87keV8tCWd1FXi09V_YZHe5qcvJ19fse3-NbrK2lqMWav1AC_UNXWzcr8A16F4aNDPvkPwCBEukB7sQIXu4Ekzbfz-80zxfHG1JnlTxrOCSw_bXygvOgrTj1Ukat9upozIiXaZnlbtGjvUg2gfSCvmeZNccHUhKnWhSGr97lpduPCZzbBv4lBbXSPQ2Tl2oh-p8FYCuhM6CbgMknbiEq0y7cFqV8b4R7-o7x_L0ezRBSgu3wRioCmSkg5X72j9_4v6k5peguoUJJtghrmlLX8hd0cn3jdVGXxB8Z2-VTECUNeVG1LhBobubn5-h5aMF263m09BwoCZ_KLxa6jMPxgSZ0xB9DLVTL8fUw7dhX1U9Nj9LoRNNQs1Dt3HYFf8PsKq-fqCoyNYyYeV6bAsaLB7biKwXPZ75I0Zj5C8eFciOViYu4hJabhFwibuKVQUfDSIeoFK5eu5_oriKL4CYI5DpmFMGqzq0XBcRtrYEG0QJQGrWyCUAp23de9L7B-6pl1V9H6nS9-_TO_pB2cn0V5tNMdMgqNTmsHaUrgJIT2M839Ymv0WHOKwYSC6E"
        ]
        -keys: array:1 [
          "authorization" => "Authorization"
        ]
      }
      -options: array:7 [
        10018 => "PHP-Curl-Class/8.8.0 (+https://github.com/php-curl-class/php-curl-class) PHP/7.4.4 curl/7.64.0"
        13 => 30
        2 => true
        20079 => Closure($ch, $header) {#154
          use: {
            $header_callback_data: {#153
              +"rawResponseHeaders": ""
              +"responseCookies": []
            }
          }
        }
        19913 => true
        10002 => ""
        10023 => array:1 [
          0 => "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIyIiwianRpIjoiZjE1OTliZDc3YTVmNmJiMjBhM2FkODk1ODhhNDRkMzM1NGU4NTQ5YTFmMTI0NzY0ZDBmZTE5NGUyZjEyMDAyYmMyZmQ4OTNhYjE0NWUyYjciLCJpYXQiOjE1OTg1MTU5MTAsIm5iZiI6MTU5ODUxNTkxMCwiZXhwIjoxNTk4NTIzMTEwLCJzdWIiOiIxYzc3ZmNlZTgzMTA0MGJkYjlhMTQ3MjhiNDFhY2EyNyIsInNjb3BlcyI6W10sImRldmljZV9pZCI6IjA0ODVmM2VjLTFkZGYtMTFlYS1iYmQzLTAwMGMyOTY4MTM5YSIsInBsYXRmb3JtIjoiV0VCIn0.NlsqP_ieVIBw13A-5IfIyUisnJSt2b6059tgaWFW0hMxmt26BTtniX9B8ep9pYxrmu-XzENREFrsAmasKlSd0_AdGN3py-khCjj-IqAXDAxWAwOX87keV8tCWd1FXi09V_YZHe5qcvJ19fse3-NbrK2lqMWav1AC_UNXWzcr8A16F4aNDPvkPwCBEukB7sQIXu4Ekzbfz-80zxfHG1JnlTxrOCSw_bXygvOgrTj1Ukat9upozIiXaZnlbtGjvUg2gfSCvmeZNccHUhKnWhSGr97lpduPCZzbBv4lBbXSPQ2Tl2oh-p8FYCuhM6CbgMknbiEq0y7cFqV8b4R7-o7x_L0ezRBSgu3wRioCmSkg5X72j9_4v6k5peguoUJJtghrmlLX8hd0cn3jdVGXxB8Z2-VTECUNeVG1LhBobubn5-h5aMF263m09BwoCZ_KLxa6jMPxgSZ0xB9DLVTL8fUw7dhX1U9Nj9LoRNNQs1Dt3HYFf8PsKq-fqCoyNYyYeV6bAsaLB7biKwXPZ75I0Zj5C8eFciOViYu4hJabhFwibuKVQUfDSIeoFK5eu5_oriKL4CYI5DpmFMGqzq0XBcRtrYEG0QJQGrWyCUAp23de9L7B-6pl1V9H6nS9-_TO_pB2cn0V5tNMdMgqNTmsHaUrgJIT2M839Ymv0WHOKwYSC6E"
        ]
      ]
      -jsonDecoderArgs: []
      -jsonPattern: "/^(?:application|text)\/(?:[a-z]+(?:[\.-][0-9a-z]+){0,}[\+\.]|x-)?json(?:-[a-z]+)?/i"
      -xmlDecoderArgs: []
      -xmlPattern: "~^(?:text/|application/(?:atom\+|rss\+|soap\+)?)xml~i"
      -defaultDecoder: null
      +"headerCallbackData": {#153}
    }
    #errorHandlers: array:6 [
      0 => Cristal\ApiWrapper\Exceptions\Handlers\NetworkErrorHandler {#150
        +tries: 0
        #transport: Cristal\ApiWrapper\Transports\Bearer {#149}
        #maxTries: 3
      }
      401 => Cristal\ApiWrapper\Exceptions\Handlers\UnauthorizedErrorHandler {#151
        +tries: 0
        #transport: Cristal\ApiWrapper\Transports\Bearer {#149}
        #maxTries: 3
      }
      403 => Cristal\ApiWrapper\Exceptions\Handlers\ForbiddenErrorHandler {#148
        +tries: 0
        #transport: Cristal\ApiWrapper\Transports\Bearer {#149}
        #maxTries: 3
      }
      404 => Cristal\ApiWrapper\Exceptions\Handlers\NotFoundErrorHandler {#156
        +tries: 0
        #transport: Cristal\ApiWrapper\Transports\Bearer {#149}
        #maxTries: 3
      }
      400 => Cristal\ApiWrapper\Exceptions\Handlers\BadRequestErrorHandler {#157
        +tries: 0
        #transport: Cristal\ApiWrapper\Transports\Bearer {#149}
        #maxTries: 3
      }
      422 => Cristal\ApiWrapper\Exceptions\Handlers\BadRequestErrorHandler {#158
        +tries: 0
        #transport: Cristal\ApiWrapper\Transports\Bearer {#149}
        #maxTries: 3
      }
    ]
  }
}

J'ai le même Wrapper pour les deux models

Hum, ok ok, indeed, after checking, there may be a problem in the documentation. Try to define different $api properties on your Video and Stream models. For example :

class Video extends Model
{
    protected static $api = 'video'; // for example
}

and,

class Stream extends Model
{
    protected static $api = 'stream'; // for example
}

Please, tell me, if it's works.

(careful your token is shown in trace)

Yes, It works ! Many thanks for your help.

ps: Token was altered before posting and only valid in my local environment but thank you for your benevolence.

Great work, many thanks for this package !

Nice 👍