Usando o driver do MongoDB com PHP

Conhecendo-se como usar o MongoDB pelo Mongo shell, podemos partir para uma aventura mais interessante através dos drivers oficiais para várias linguagens de programação. Nesse post será mostrado como realizar as principais operações através do driver feito para PHP.

##Instalando o driver do MongoDB para PHP Para instalar o driver é necessário ter o PEAR instalado para que possamos baixá-lo usando o PECL. Para instalá-lo é fácil:

1
sudo apt-get install php5-dev php5-cli php-pear

Depois de instalar o PEAR, você poderá baixar o driver oficial do MongoDB através do repositório do PECL:

1
sudo pecl install mongo

Só falta mais um passo: ativar o driver globalmente na sua instalação do PHP através do php.ini. Use o comando find / -name 'php.ini' em seu terminal, caso não saiba onde o arquivo php.ini está. Depois de encontrá-lo, abra-o em um editor e adicione a seguinte linha (de preferência na seção de extensions):

1
extension=mongo.so

Reinicie o servidor:

1
2
3
4
5
# para nginx
sudo service nginx restart

#para Apache
sudo service apache2 restart

Crie um arquivo index.php na raiz da pasta pública do seu servidor com o seguinte conteúdo:

1
2
<?php
phpinfo();

Abra o browser em http://localhost/ e verifique na página que abriu se há uma seção mostrando detalhes sobre o driver do MongoDB. Se tudo ocorreu corretamente, corra para o abraço!

##Conectando o MongoDB à sua aplicação PHP Primeiro inicie seu servidor MongoDB através do terminal com o comando mongod. Ele ficará aguardando conexões na porta 27017 (padrão).

Crie um script PHP com o seguinte conteúdo (vou omitir a tag de abertura do PHP, assumindo que você já sabe que ela existe):

1
$conn = new MongoClient();

Agora a variável $conn possui uma referência para a conexão com o servidor do MongoDB rodando em sua máquina na porta padrão. Caso queira especificar outro caminho para o servidor, passe ele como parâmetro no método MongoClient:

1
$conn = new MongoClient('192.168.25.2:27018');

A partir da variável $conn podemos acessar o MongoDB de forma similar ao acesso de uma base de dados relacional usando um ORM. Para escolher uma banco de dados, simplesmente apontamos para ela como se fosse um atributo da classe MongoClient que acabamos de instanciar:

1
2
3
4
5
6
7
$conn = new MongoClient();

// acessando a base de dados "blog"
$conn->blog;

// de forma alternativa
$conn->selectDB("blog");

A primeira forma $conn->blog é a mais usual para acessar um banco, contudo ela é inviável para o caso de um banco chamado erp.blog, por exemplo, daí o método $conn->selectDB() é útil.

De forma parecida com que acessamos um banco podemos acessar também uma coleção:

1
2
3
4
5
6
7
8
9
10
$conn = new MongoClient();

// acessando a coleção "posts"
$conn->blog->posts;

// de forma alternativa
$conn->selectDB('blog')->selectCollection('posts')

// ou também
$conn->blog->selectCollection('posts')

Se lembra da ideia de que o MongoDB é schemaless? Sabendo disso, quando acessamos o nome de um banco ou de uma coleção inexistente, estamos de fato criando-as de forma implícita. Essa é uma das maiores dores de cabeça de quem está começando a usar os drivers do MongoDB sem conhecê-lo.

Para exemplificar esse problema veja o código abaixo:

1
$conn->blogui->posts->insert(array());

Viu o problema? Nesse código o driver solicitou ao servidor que fosse criado o banco chamado “blogui” para inserir um novo documento ao invés de usar o banco “blog” que já tinhamos criado. Por isso esteja sempre atento ao seu código!

##Representação dos documentos no PHP Um documento no MongoDB é um objeto binário BSON, que pode ser visto em alto nível como um objeto JSON. Os objetos JSON no PHP são mapeados para arrays. Por exemplo, o objeto JSON abaixo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
  "autor": "Jordan Kobellarz",
  "titulo": "Aventuras com o MongoDB",
  "conteudo": "bla bla bla",
  "tags": ["mongodb", "nosql", "documentos"],
  "comentarios": [
    {
      "autor":"Tobias",
      "comentario": "Eu adorei o artigo. Parabéns!"
    },
    {
      "autor": "Emanuel",
      "comentario": "Estou usando o MongoDB em produção, muito bom!"
    }
  ]
}

A mesma estrutura do objeto do exemplo acima ficaria assim no PHP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$doc = [
  "autor" => "Jordan Kobellarz",
  "titulo" => "Aventuras com o MongoDB",
  "conteudo" => "bla bla bla",
  "tags" => ["mongodb", "nosql", "documentos"],
  "comentarios" => [
    [
      "autor" => "Tobias",
      "comentario" => "Eu adorei o artigo. Parabéns!"
    ],
    [
      "autor" => "Emanuel",
      "comentario" => "Estou usando o MongoDB em produção, muito bom!"
    ]
  ]
];

Lembre-se que a criação de arrays no PHP é feita de maneira implícita usando apenas colchetes somente para as versões do PHP acima da 5.4. Caso a versão do PHP que você esteja usando seja menor que 5.4, use a notação array().

##Operações CRUD Os mesmos métodos que são acessados no topo de uma coleção através do terminal também estão presentes para acesso através do driver PHP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$conn = new MongoClient();
$db = $conn->blog;

$doc = [
  "nome" => "Jordan",
  "titulo" => "Hello World!"
];

// inserindo um documento
$db->posts->insert($doc);

// procurando um documento
$db->posts->findOne(['nome' => 'Jordan']);

// removendo um documento
$db->posts->remove(['titulo' => 'Hello World!']);

Os métodos usados no topo de uma coleção pelo driver têm a mesma quantidade de parâmetros que os métodos acessados pelo terminal. Por exemplo, para projetar os campos que queremos retornar no metodo find:

1
2
3
4
$query = ['nome' => 'MongoDB'];
$projecao = ['_id' => 0];

$db->posts->findOne($query, $projecao);

É possível também encadear métodos no topo do método find, quando queremos fazer uma ordenação, limitar ou contar os resultados, por exemplo:

1
$db->posts->find([])->sort(['nome' => 1])->count();

Os modificadores de consulta iniciados com $ também podem ser passados da mesma forma que o usamos no Mongo shell, mas aqui devemos ter o cuidado de passá-los com aspas simples, caso contrário o PHP interpretará como uma variável:

1
2
3
4
5
$db->posts->find([
  "nome" => [
    '$ne' => 'Mongo'
  ]
]);

##Mapeando SQL para os comandos do MongoDB

No manual do driver do MongoDB para PHP encontramos um mapeamento de comandos SQL para comandos do MongoDB. Veja abaixo alguns exemplos:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// INSERT INTO USERS VALUES(1,1)
$db->users->insert(["a" => 1, "b" => 1]);

// SELECT a,b FROM users
$db->users->find([], ["a" => 1, "b" => 1]);

//SELECT * FROM users WHERE age=33
$db->users->find(["age" => 33]);

// SELECT a,b FROM users WHERE age=33
$db->users->find(["age" => 33], ["a" => 1, "b" => 1]);

// SELECT a,b FROM users WHERE age=33 ORDER BY name
$db->users->find(["age" => 33], ["a" => 1, "b" => 1])->sort(["name" => 1]);

// SELECT * FROM users WHERE age>33
$db->users->find(["age" => ['$gt' => 33]]);

// SELECT * FROM users WHERE age<33
$db->users->find(["age" => ['$lt' => 33]]);

// SELECT * FROM users WHERE name LIKE "%Joe%"
$db->users->find(["name" => new MongoRegex("/Joe/")]);

// SELECT * FROM users WHERE name LIKE "Joe%"
$db->users->find(["name" => new MongoRegex("/^Joe/")]);

// SELECT * FROM users WHERE age>33 AND age<=40
$db->users->find(["age" => ['$gt' => 33, '$lte' => 40]]);

// SELECT * FROM users ORDER BY name DESC
$db->users->find()->sort(["name" => -1]);

// CREATE INDEX myindexname ON users(name)
$db->users->ensureIndex(["name" => 1]);

// CREATE INDEX myindexname ON users(name,ts DESC)
$db->users->ensureIndex(["name" => 1, "ts" => -1]);

// SELECT * FROM users WHERE a=1 and b='q'
$db->users->find(["a" => 1, "b" => "q"]);

// SELECT * FROM users LIMIT 20, 10
$db->users->find()->limit(10)->skip(20);

// SELECT * FROM users WHERE a=1 or b=2
$db->users->find(['$or' => [["a" => 1], ["b" => 2]]]);

// SELECT * FROM users LIMIT 1
$db->users->find()->limit(1);

// EXPLAIN SELECT * FROM users WHERE z=3
$db->users->find(["z" => 3])->explain()

// SELECT DISTINCT last_name FROM users
$db->command(["distinct" => "users", "key" => "last_name"]);

// SELECT COUNT(*) FROM users
$db->users->count();

// SELECT COUNT(age) from users
$db->users->find(["age" => ['$exists' => true]])->count();

// UPDATE users SET a=1 WHERE b='q'
$db->users->update(["b" => "q"], ['$set' => ["a" => 1]]);

// UPDATE users SET a=a+2 WHERE b='q'
$db->users->update(["b" => "q"], ['$inc' => ["a" => 2]]);

// DELETE FROM users WHERE z="abc"
$db->users->remove(["z" => "abc"]);