Jak zacząć z MongoDB na Laravelu
Jakiś czas temu opublikowałem następujący artykuł: Konfiguracja MongoDB dla Laravela w ploi.io. ploi.io jest świetne, gdy musisz dokonać nieskomplikowanego deploymentu aplikacji na produkcję. Tym razem jednak porozmawiamy o MongoDB na Laravelu. NoSQL (MongoDB) nie jest zbyt popularne, jeżeli chodzi o Laravela, ponieważ niewielu używa tam Mongo zamiast SQL. SQL jest w zasadzie wbudowane w Laravela i można dzięki niemu szybko zrozumieć, jak działają tam bazy danych.
Jeżeli interesują Cię wady i zalety NoSQL, to zachęcam do zapoznania się z wyżej wspomnianym artykułem. Tym razem po krótce opiszę każdą wadę i zaletę wspomnianą w poprzednim artykule, podając tym samym przykłady na temat tego, jak różne rzeczy działają, jak używać ich lokalnie i jaki mają wpływ na środowisko produkcyjne.
Zaczynając jednak od początku — musisz znać Laravel Eloquent ORM, aby móc zrobić cokolwiek więcej.
Warunki wstępne i działanie lokalne
Zdecydowanie musisz mieć MongoDB na swojej maszynie. Zachęcam do uruchomienia Valet albo Homestead na swoim lokalnym urządzeniu, ponieważ tak jest łatwiej. Jeżeli chodzi o MongoDB, to ja pracowałem tylko z Homestead.
Jeżeli możesz pracować z Homestead, dodaj mongodb: true
do swojego pliku Homestead.yaml
. Oto moja konfiguracja dla Homestead:
ip: 192.168.10.13
memory: 4096
cpus: 2
mongodb: true
mariadb: true
provider: virtualbox
authorize: ~/.ssh/id_rsa.pub
keys:
- ~/.ssh/id_rsa
folders:
-
map: 'C:\Laravel\homestead'
to: /home/vagrant/code
sites:
-
map: homestead.test
to: /home/vagrant/code/public
php: "7.2"
schedule: true
databases:
- homestead
name: homestead
hostname: homestead
Zachęcam również do używania komendy vendor/bin/homestead make
zamiast kopiowania konfiguracji, którą podałem jako przykład, ponieważ ścieżka do folderu powinna być automatycznie generowana przez powyższą komendę. Chciałem Wam po prostu pokazać, że mam u siebie mongodb: true
.
Następnie musisz ponownie uruchomić provisioning na swojej maszynie albo odpalić Homestead z --provision
, jeśli nie używało się wcześniej up
.
Możesz się do niej dostać przez localhost
(jako host) i homestead
/ secret
(jako użytkownik i hasło).
Eloquent ORM dla NoSQL
Ten pakiet MongoDB jest całkiem popularny. Być może istnieje więcej pakietów, które obsługują NoSQL, ale najbardziej ufamy takim, które buduje społeczność. Odpalałem go również samemu na produkcji i do tej pory nie miałem żadnych problemów. Konfiguracja jest prosta, jeśli przeczyta się dokumentację — pomoże Ci ona zainstalować nasz pakiet przez Composer i skonfigurować sterowniki bazy danych. Skupimy się tutaj jednak na ważnych rzeczach.
Nasz pakiet oferuje model Moloquent
. Jest to model Eloquent, ale skonstruowany specjalnie dla Mongo. Jeśli pracowało się trochę z ORM, to pewnie następujący widok nie jest Ci obcy:
class Post extends Model
{
//
}
W tym przypadku nie będziemy rozszerzać klasy Model, czyli podstawowego modelu od Laravela.
use Jenssegers\Mongodb\Eloquent\Model;
class Post extends Model
{
//
}
SQL działa z tabelami, a NoSQL z kolekcjami. Zamiast zmiennej $table
mamy zmienną $collection
. Warto również zauważyć, że klucza podstawowego nie można ustawić za pomocą $primaryKey
, a $incrementing
jest niedostępne. Warto też określić, że model należy do połączenia mongodb
, które stworzyliśmy wcześniej:
use Jenssegers\Mongodb\Eloquent\Model;
class Post extends Model
{
protected $connection = 'mongodb';
}
Musisz pogodzić się z faktem, że MongoDB automatycznie przypisuje klucze do dokumentów (odpowiednik wierszy w SQL). Aby otrzymać dostęp do głównego klucza dokumentu, musisz mieć taką samą nazwę atrybutu, jak w podstawowym modelu.
echo 'The post ID is: '. $post->_id;
echo 'The post ID is '. $post->id; // they both work
find()
używa pola klucza głównego, aby zwrócić wynik:
$post = Post::find('517c43667db388101e00000f');
Prawie całkowity brak schematów
W NoSQL nie mamy schematów do zdefiniowania. Możemy skorzystać z migracji, aby zdefiniować indeksy, unikalne pola i inne pola specyficzne dla mongo:
Schema::create('posts', function ($collection) {
$collection->index('slug');
$collection->unique('slug');
});
Proces migracji wygląda tam tak samo. Aby odpalić migracje, upewnij się, że domyślne ustawienie sterowników (zmienna DB_CONNECTION env
) jest ustawione na mongodb.
php artisan migrate
Co możemy zrobić w przypadku, gdy chcemy odpalić zarówno SQL, jak i NoSQL w tym samym projekcie:
- przenieś migracje SQL do folderu znajdującego się wewnątrz folderu z migracjami. Ja go nazwę
mysql
- Wszystkie modele, które działają z czymś innym niż domyślnym sterownikiem bazy danych powinny rozszerzy właściwy model
Uruchamianie migracji powinno się odbywać dwojako, jeśli chodzi o uruchomienie lokalne albo na produkcji. Dla sterownika domyślnego (tj. gdy DB_CONNECTION
jest ustawione na mongodb
):
php artisan migrate
Dla drugiego sterownika (np. dla MySQL):
php artisan migrate --database=mysql --path=database/migrations/mysql/
Uwierzytelnianie użytkowników za pomocą MongoDB
Duży problem, który napotkałem z MongoDB, dotyczył elementu User
. Rozszerza on model Authenticatable
. MongoDB nie obsługuje domyślnego modelu oferowanego przez Laravela, więc będziemy musieli użyć tego, który jest dołączony do pakietu:
use Jenssegers\Mongodb\Auth\User as Authenticatable;
class User extends Authenticatable
{
//
}
Jeśli chcesz używać Laravel Passport z MongoDB, upewnij się, że używasz designmynight/laravel-mongodb-passport. Zapewni Ci to kolejną klasę Authenticatable
, która wykona zadanie za Ciebie!
NoSQL = NoJoins
NoSQL jest z kilku powodów lepszy od SQL: lepsza elastyczność, szybkość, skalowalność i integralność. Jedynym minusem jest to, że nie mamy odpowiednika join
. Nie możemy użyć trzeciego zbioru do połączenia danych między dwoma innymi zbiorami. Jednak w naszym pakiecie jest kilka niespodzianek.
Na przykład, relacja belongsToMany
wyglądałaby następująco:
class User extends Model
{
public function roles()
{
return $this->belongsToMany('App\Role');
}
}
class Role extends Model
{
public function users()
{
return $this->belongsToMany('App\User');
}
}
Niezależnie od tego, czy próbujesz uzyskać dostęp do $user->roles
, czy $role->users
, to będziesz potrzebować trzeciej tabeli o nazwie role_user
, która przechowuje dane.
W tym przypadku MongoDB robi coś paskudnego: umożliwia many-to-many, ale nie potrzebuje trzeciej tabeli — po prostu przechowuje identyfikatory we własnych modelach. Upewnij się, że jeśli chcesz ustawić niestandardowe klucze obce, ustaw drugi parametr funkcji belongsToMany
na null
.
class User extends Model
{
public function roles()
{
return $this->belongsToMany('App\Role', null, 'users_ids', 'roles_ids');
}
}
class Role extends Model
{
public function users()
{
return $this->belongsToMany('App\User', null, 'roles_ids', 'users_ids');
}
}
$role = Role::named('admin')->first();
$user->attach($role);
$role->attach($user);
Po zakończeniu powyższego otrzymasz coś takiego:
Dokument User
wygląda następująco:
{
"_id": "user_id_here",
"username": "dummy",
...,
"roles_ids": [
"role_id_here"
]
}
Dokument Role
wygląda następująco:
{
"_id": "role_id_here",
"name": "administrator",
...,
"users_ids": [
"user_id_here"
]
}
Reszta innych relacji, wyłączając morphToMany
, działa tak samo, jak w SQL.
Schemat daje Ci możliwość zabawy
Dopóki NoSQL nie ma schematów, to możemy je zdefiniować podczas pracy z danymi, prawda? W MongoDB możemy wyrzucać pola, kiedy tylko chcemy.
Oto nasz dokument:
{
"_id": "...",
"name": "John",
"password": "...",
"old_field1": "value1",
"old_field2": "value2",
}
Jeśli chcemy wyrzucić trochę pól, to możemy to zrobić przy użyciu metody drop
:
$john = User::where('name', 'John')->first();
$john->drop('old_field1');
// or
$john->drop(['old_field1', 'old_field2']); // works with more
W końcu usunie ona pole z naszego dokumentu:
{
"_id": "...",
"name": "John",
"password": "..."
}
Wprowadzanie na produkcję
Od jakiegoś już czasu używam MongoDB na produkcji. Prawda jest taka, że przy podstawowym użyciu ORM z małą konfiguracją MongoDB, tak jak opisałem to w artykule wspomnianym na początku, działa to bezproblemowo.
MongoDB jest szybki i niezawodny nawet w przypadku aplikacji o dużym natężeniu ruchu. Średnio zużywa około 4 GB pamięci RAM przy około 600 milionach odczytów, zapisów i usuwaniu dokumentów w ciągu całego miesiąca, przy średniej łącznej liczbie 600 zapisów i odczytów na sekundę.
Nie polecam używania go do cache'owania — ilekroć chcesz to robić, pamiętaj tylko o używaniu memory over disk — zapewnia to mniejsze tarcie i większą szybkość dostępu i pisania.
Oryginał tekstu w języku angielskim możesz przeczytać tutaj.