Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Tincan Laravel Package

rusticisoftware/tincan

PHP library for implementing the Experience API (Tin Can/xAPI). Provides tools to build and send statements, integrate with an LRS, and work with xAPI data. Install via Composer, includes PHPUnit tests and phpDocumentor docs generation.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require rusticisoftware/tincan
    

    Requires PHP 5.5+ (PHP 7+ recommended).

  2. Basic Statement Creation:

    use TinCan\Statement;
    use TinCan\Agent;
    use TinCan\Activity;
    use TinCan\Verb;
    use TinCan\RemoteLRS;
    
    // Initialize RemoteLRS with your LRS endpoint and credentials
    $lrs = new RemoteLRS('https://your-lrs-endpoint.com/xapi/statements/', '1.0.3', 'username', 'password');
    
    // Create an Agent (learner)
    $agent = new Agent('http://example.com/agents/123', 'http://example.com/agents/123#me', 'John Doe');
    
    // Create an Activity (learning activity)
    $activity = new Activity('http://example.com/activities/456', 'http://adlnet.gov/expapi/activities/course');
    
    // Create a Verb (action)
    $verb = new Verb('http://adlnet.gov/expapi/verbs/completed');
    
    // Create and send a Statement
    $statement = new Statement($agent, $verb, $activity);
    $result = $lrs->sendStatement($statement);
    
  3. First Use Case: Track a user's interaction with a learning activity (e.g., completing a course module). Use the Statement class to encapsulate the event, then send it to your LRS.

Where to Look First

  • Official Documentation for API reference and examples.
  • tests/ directory for real-world usage patterns and edge cases.
  • TinCan/Statement.php for core statement construction logic.
  • TinCan/RemoteLRS.php for LRS communication details (authentication, headers, error handling).

Implementation Patterns

Core Workflows

1. Building Statements

  • Immutable Pattern: Use fluent methods to chain modifications:
    $statement = (new Statement($agent, $verb, $activity))
        ->withResult(new Result('completed', ['score': '1.0']))
        ->withContext(new Context(['extensions': ['http://example.com/extensions/foo' => 'bar']]));
    
  • Versioning: Explicitly set the xAPI version when serializing:
    $json = $statement->asVersion('1.0.3')->toJSON();
    

2. Handling Attachments

  • Attach files (e.g., PDFs, images) to statements:
    $attachment = new Attachment('file:///path/to/file.pdf', 'application/pdf');
    $statement = $statement->withAttachment($attachment);
    
  • For remote files, use file:// or http:// URLs:
    $attachment = new Attachment('http://example.com/file.pdf');
    

3. Querying Statements

  • Fetch statements with filters:
    $statements = $lrs->queryStatements([
        'agent' => 'http://example.com/agents/123',
        'verb' => 'http://adlnet.gov/expapi/verbs/completed',
        'since' => '2023-01-01T00:00:00Z',
        'limit' => 10
    ]);
    
  • Use If-None-Match for conditional requests (ETag support):
    $lrs->queryStatements([...], ['If-None-Match' => '*']);
    

4. Authentication and Headers

  • Basic Auth: Pass credentials to RemoteLRS constructor.
  • Custom Headers: Override default headers per request:
    $lrs->sendStatement($statement, ['X-Custom-Header' => 'value']);
    
  • Proxy Support: Configure proxy settings:
    $lrs = new RemoteLRS($endpoint, $version, $username, $password, [
        'proxy' => ['http' => 'http://proxy.example.com:8080']
    ]);
    

5. Error Handling

  • Wrap LRS calls in try-catch blocks:
    try {
        $result = $lrs->sendStatement($statement);
    } catch (TinCan\Exception\TinCanException $e) {
        Log::error('LRS Error: ' . $e->getMessage());
    }
    
  • Check Result objects for success:
    if ($result->isSuccess()) {
        // Handle success
    }
    

Laravel-Specific Patterns

1. Service Provider Integration

  • Register the LRS client in AppServiceProvider:
    public function register()
    {
        $this->app->singleton('tincan.lrs', function ($app) {
            return new RemoteLRS(
                config('tincan.lrs.endpoint'),
                config('tincan.lrs.version'),
                config('tincan.lrs.username'),
                config('tincan.lrs.password')
            );
        });
    }
    
  • Publish config:
    php artisan vendor:publish --provider="YourServiceProvider"
    

2. Middleware for Tracking

  • Create middleware to log xAPI statements on user actions:
    public function handle($request, Closure $next)
    {
        $response = $next($request);
    
        if ($request->user() && $request->is('learning/*')) {
            $statement = (new Statement(
                $request->user()->agent(),
                new Verb('http://example.com/verbs/accessed'),
                new Activity('http://example.com/activities/' . $request->path())
            ))->withContext(new Context([
                'extensions' => ['page' => $request->path()]
            ]));
    
            app('tincan.lrs')->sendStatement($statement);
        }
    
        return $response;
    }
    

3. Artisan Commands for Testing

  • Test LRS connectivity via CLI:
    // app/Console/Commands/TestLRS.php
    public function handle()
    {
        $lrs = app('tincan.lrs');
        $statement = new Statement(
            new Agent('http://example.com/agents/test'),
            new Verb('http://adlnet.gov/expapi/verbs/test'),
            new Activity('http://example.com/activities/test')
        );
    
        try {
            $result = $lrs->sendStatement($statement);
            $this->info('LRS Test: ' . ($result->isSuccess() ? 'Success' : 'Failed'));
        } catch (Exception $e) {
            $this->error('LRS Error: ' . $e->getMessage());
        }
    }
    

4. Event Listeners for Analytics

  • Trigger xAPI statements on model events (e.g., UserCompletedCourse):
    public function handle(UserCompletedCourse $event)
    {
        $statement = (new Statement(
            $event->user->agent(),
            new Verb('http://adlnet.gov/expapi/verbs/completed'),
            new Activity($event->course->activityId())
        ))->withResult(new Result('completed', ['score' => $event->score]));
    
        app('tincan.lrs')->sendStatement($statement);
    }
    

5. Caching Responses

  • Cache frequent queries (e.g., user activity reports):
    $cacheKey = 'xapi:statements:user:' . $userId;
    $statements = Cache::remember($cacheKey, now()->addHours(1), function () use ($lrs, $userId) {
        return $lrs->queryStatements([
            'agent' => $userId,
            'limit' => 100
        ]);
    });
    

Gotchas and Tips

Pitfalls

  1. Immutable Objects:

    • Forgetting to chain setters (e.g., $statement->withResult() instead of $statement->result = ...) will result in silent failures. Always use fluent methods.
  2. Version Mismatches:

    • Older LRS versions (e.g., 1.0.1) may reject 1.0.3 statements. Explicitly set the version:
      $statement->asVersion('1.0.1')->toJSON();
      
  3. Attachment Handling:

    • File paths in Attachment must be absolute or use file:// protocol. Relative paths will fail:
      // ❌ Fails
      $attachment = new Attachment('file.pdf');
      
      // ✅ Works
      $attachment = new Attachment('file:///var/www/file.pdf');
      
  4. ETag Issues:

    • If-None-Match: * may not work as expected with all LRS implementations. Test thoroughly.
  5. UUID Validation:

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport
twbs/bootstrap4