Desafio PHP

Organização com Composer e PSR-4

Enunciado

Configure um projeto Composer com autoload PSR-4 para o namespace App\ apontando para src/. Crie as classes App\Models\Product, App\Repositories\ProductRepository e App\Services\ProductService. O ProductRepository deve armazenar produtos em um array e ter métodos add(Product $product) e all(): array. O ProductService deve receber o repositório no construtor e ter um método listNames(): array que retorna os nomes dos produtos. No arquivo principal, instancie tudo, adicione dois produtos e exiba a lista de nomes separados por vírgula.

Requisitos

  • O composer.json define autoload.psr-4 com App\\ mapeado para src/.
  • As classes estão nos namespaces e diretórios corretos.
  • O ProductService depende do ProductRepository via injeção no construtor.
  • O programa principal usa o autoload do Composer (via vendor/autoload.php).

Código inicial

<?php
// Configure o composer.json, crie as classes e o arquivo principal

Saída esperada

Notebook, Mouse
Ver dica

Após criar o composer.json, execute composer dump-autoload para gerar o autoloader. No código, use require 'vendor/autoload.php';.

Mostrar solução
{
  "autoload": {
    "psr-4": {
      "App\\": "src/"
    }
  }
}
<?php
// src/Models/Product.php
namespace App\Models;

class Product
{
    public function __construct(private string $name) {}

    public function getName(): string
    {
        return $this->name;
    }
}
<?php
// src/Repositories/ProductRepository.php
namespace App\Repositories;

use App\Models\Product;

class ProductRepository
{
    /** @var Product[] */
    private array $products = [];

    public function add(Product $product): void
    {
        $this->products[] = $product;
    }

    /** @return Product[] */
    public function all(): array
    {
        return $this->products;
    }
}
<?php
// src/Services/ProductService.php
namespace App\Services;

use App\Repositories\ProductRepository;

class ProductService
{
    public function __construct(private ProductRepository $repository) {}

    public function listNames(): array
    {
        return array_map(
            fn($product) => $product->getName(),
            $this->repository->all()
        );
    }
}
<?php
// index.php
require 'vendor/autoload.php';

use App\Models\Product;
use App\Repositories\ProductRepository;
use App\Services\ProductService;

$repository = new ProductRepository();
$repository->add(new Product('Notebook'));
$repository->add(new Product('Mouse'));

$service = new ProductService($repository);
echo implode(', ', $service->listNames()) . PHP_EOL;

Passo a passo

  1. Criamos o composer.json com a seção autoload.psr-4 mapeando App\\ para src/.
  2. Executamos composer dump-autoload para gerar o arquivo vendor/autoload.php.
  3. A classe Product fica em src/Models/Product.php com namespace App\Models.
  4. O ProductRepository armazena produtos em um array e oferece add e all.
  5. O ProductService recebe o repositório via construtor e usa array_map para extrair os nomes.
  6. No index.php, incluímos o autoload do Composer e usamos as classes.
  7. Adicionamos dois produtos, criamos o serviço e exibimos os nomes separados por vírgula.

Por que funciona

O autoload do Composer segue PSR-4: o namespace App\Models\Product é convertido para src/Models/Product.php. A injeção de dependência no ProductService desacopla as camadas, facilitando testes e manutenção.

Erros comuns

  • Esquecer de rodar composer dump-autoload após alterar o composer.json: o autoloader não é atualizado.
  • Colocar arquivos fora do diretório mapeado: o Composer não encontrará a classe.
  • Usar require manual além do vendor/autoload.php: pode causar inclusão duplicada.

Outra forma de resolver

Sem Composer, você pode implementar um autoloader manual como no exercício anterior, mas o Composer oferece vantagens como otimização e gerenciamento de dependências.

Saída esperada

Notebook, Mouse