Skip to main content

Overview

Sometimes you want to define the extraction schema using one class but receive the result as a different class. This is useful when:
  • You have a rich domain model for the LLM schema but want a simpler DTO for output
  • You want to separate API contracts from internal representations
  • You need different validation rules for input vs output
The intoInstanceOf() method allows you to specify a different target class for deserialization while keeping the original class for schema generation.

Example

<?php
require 'examples/boot.php';

use Cognesy\Instructor\StructuredOutput;

// Rich schema class with detailed structure (sent to LLM)
class UserProfile {
    public string $fullName;
    public int $age;
    public string $email;
    public string $phoneNumber;
    public string $address;
}

// Simplified DTO for output (subset of fields you need)
class UserDTO {
    public function __construct(
        public string $fullName = '',
        public string $email = '',
    ) {}
}

// Extract using UserProfile schema, but receive as UserDTO
$user = (new StructuredOutput)
    ->withResponseClass(UserProfile::class)      // Schema sent to LLM
    ->intoInstanceOf(UserDTO::class)             // Output class
    ->with(
        messages: "Extract: John Smith, 30 years old, [email protected], phone: 555-1234, lives at 123 Main St",
    )
    ->get();

dump($user);

// Result is UserDTO instance (not UserProfile)
assert($user instanceof UserDTO);
assert(!($user instanceof UserProfile));

// UserDTO has only the fields it needs
assert($user->fullName === 'John Smith');
assert($user->email === '[email protected]');

// UserDTO doesn't have the extra fields from UserProfile
assert(!property_exists($user, 'age'));
assert(!property_exists($user, 'phoneNumber'));
assert(!property_exists($user, 'address'));

echo "\nExtracted as UserDTO:\n";
echo "Name: {$user->fullName}\n";
echo "Email: {$user->email}\n";
?>

Expected Output

object(UserDTO)#123 (2) {
  ["fullName"]=>
  string(10) "John Smith"
  ["email"]=>
  string(17) "[email protected]"
}

Extracted as UserDTO:
Name: John Smith
Email: [email protected]

Note

The LLM receives the UserProfile schema (with 5 fields: name, age, email, phone, address), but the result is deserialized into UserDTO (with only 2 fields: name, email). Extra fields that don’t exist in UserDTO are ignored during deserialization.