Intro till Redbean

Marcus Olsson,

Redbean PHP

Om man utveckla en mini-applikation snabbt, och vill slippa att behöva sätta upp allt för avancerad logik för dina modeller – då brukar man kanske använda sig av ett ORM (Object Relation Mapper) för att underlätta och snabba upp saker och ting lite. Vare sig det är det inbyggda i sitt ramverk (Eloquent i Laravel t.ex.), Doctrine eller kanske phpActiveRecord (som jag har skrivit om tidigare).

Men nyligen – för ett litet projekt som jag genomförde åt en kund – började jag titta på Redbean som visade sig vara ett fantastiskt litet verktyg som gör det flygande enkelt och snabbt (kanske lite väl enkelt) att jobba mot databasen.

Innan du läser vidare och funderar på att använda det i produktion – ha i åtanke på att användarbasen för Redbean är relativt liten, och har endast en enda aktiv utvecklare – så dokumentation är bristfällig på sina platser. Dessutom är Redbean som sagt nästan för enkel att jobba med då den hanterar väldigt mycket på automatik och antar hur du vill ha strukturen i din databas, du kommer att råka ut för underliga saker under utvecklingen.

Med den varningen, välkommen till PHP-världens (kanske) enklaste ORM.

Varför Redbean?

Redbean är ett litet bibliotek som endast upptar en enda fil (i alla fall om man laddar ner "all in one pack"), och som kräver nästintill noll konfiguration. Perfekt för små snabba projekt där du vill ha resultat direkt.

Att komma igång

Ladda ner Redbean från redbeanphp.com, eller klona det från Github (eller för all del, använd Composer).

För att sätta upp:

1require_once('rb.php');
2R::setup('mysql:host=localhost;dbname=' . DB_NAME, DB_USER, DB_PASSWORD);
1require_once('rb.php');
2R::setup('mysql:host=localhost;dbname=' . DB_NAME, DB_USER, DB_PASSWORD);

Och du är igång!

Objekt och relationer i Redbean

Låt oss anta att vi har ett gäng elever i en skola. Eleverna går i en klass, och klassen har flera lärare. Lärarna har i sin tur flera klasser. I "vanliga" fall skulle vi behöva tänka ut något smart sätt att sätta upp de här relationerna, men Redbean skapar dessa åt oss i bara farten.

Låt oss börja med skolan:

1$school = R::dispense('school');
2$school->name = 'Jedi Academy';
3$school->city = 'Coruscant';
4R::store($school);
1$school = R::dispense('school');
2$school->name = 'Jedi Academy';
3$school->city = 'Coruscant';
4R::store($school);

Vad gjorde vi där egentligen? Vi skapade ett skolobjekt, lade till attribut och sparade den. Kolla efter i din databas, tabellen "school" är skapad, och i den hittar vi...

1id name city
21 Jedi Academy Coruscant
1id name city
21 Jedi Academy Coruscant

Så enkelt var det.

Nu vill vi ha ett par elever och en klass:

1// Skapa två elever
2list($s1, $s2) = R::dispense('student', 2);
3$s1->name = 'Anakin Solo';
4$s1->age = 11;
5$s2->name = 'Ben Skywalker';
6$s2->age = 11;
7R::store($s1);
8R::store($s2);
9 
10// Klass
11$class = R::dispense('class');
12$class->name = '4B';
13 
14// Länka ihop elever med klass
15$class->ownStudent[] = $s1;
16$class->ownStudent[] = $s2;
17 
18R::store($class);
1// Skapa två elever
2list($s1, $s2) = R::dispense('student', 2);
3$s1->name = 'Anakin Solo';
4$s1->age = 11;
5$s2->name = 'Ben Skywalker';
6$s2->age = 11;
7R::store($s1);
8R::store($s2);
9 
10// Klass
11$class = R::dispense('class');
12$class->name = '4B';
13 
14// Länka ihop elever med klass
15$class->ownStudent[] = $s1;
16$class->ownStudent[] = $s2;
17 
18R::store($class);

Inspekterar du tabellen "student" i databasen nu så ser du att det har lagts till en foreign key, class_id som pekar på rätt klass.

Men nu till det riktigt roliga, "n:n" (many-to-many) relationer. Som vi tidigare har sagt så ska läraren kunna ha flera klasser, och en klass ska kunna ha flera lärare.

1// Skapa lärare
2$teacher = R::dispense('teacher');
3$teacher->name = 'Luke Skywalker';
4R::store($teacher);
5 
6// Ladda klass där id = 1
7$class = R::load('class', 1);
8// Länka ihop klass och lärare
9$class->sharedTeacher[] = $teacher;
10R::store($class);
1// Skapa lärare
2$teacher = R::dispense('teacher');
3$teacher->name = 'Luke Skywalker';
4R::store($teacher);
5 
6// Ladda klass där id = 1
7$class = R::load('class', 1);
8// Länka ihop klass och lärare
9$class->sharedTeacher[] = $teacher;
10R::store($class);

Du börjar säkert förstå vad du kan förvänta dig nu; Redbean har skapat en "pivot table" i databsen åt oss; class_teacher.

Men hur kan vi t.ex. hämta data på se vilka klasser som en viss lärare undervisar? Så enkelt som:

1$teacher = R::load('teacher', 1);
2$classes = $teacher->sharedClass;
3foreach($classes as $class) {
4 echo $class->name;
5}
6 
7// 4B
1$teacher = R::load('teacher', 1);
2$classes = $teacher->sharedClass;
3foreach($classes as $class) {
4 echo $class->name;
5}
6 
7// 4B

Busenkelt.

Redbean och SQL

Traditionella sökningar med vilkor gör man med hjälp av SQL, med PDO-syntax:en. T.ex. för att hitta klassen "4B" om vi inte känner till dess ID:

1$class = R::find('class', 'name = ?', array(
2 '4B'
3));
1$class = R::find('class', 'name = ?', array(
2 '4B'
3));

Även funktionen R::findOne() kan användas om man endast vill ha/förväntar sig en rad.

"LIMIT", "ORDER BY" och andra direktiv kan man slänga in där precis som man förväntar sig:

1$class = R::find('class', 'name = ? ORDER BY name ASC LIMIT ?', array(
2 $name,
3 $limit
4));
1$class = R::find('class', 'name = ? ORDER BY name ASC LIMIT ?', array(
2 $name,
3 $limit
4));

Modeller

Om man vill kan man också använda sig av modeller tillsammans med Redbean. Kanske vill man att varje gång man skapar ett inägg i databasen så ska fältet created_at skapas och sätta en timestamp:

1class Model_class extends RedBean_SimpleModel {
2 public function update() {
3 $this->created_at = date("Y-m-d H:i:s");
4 }
5}
1class Model_class extends RedBean_SimpleModel {
2 public function update() {
3 $this->created_at = date("Y-m-d H:i:s");
4 }
5}

Som du ser så kan man ganska enkelt lägga in valideringen direkt i modellen också via update() metoden.

Det "farliga" med Redbean

Jag nämnde i början att Redbean kanske inte lämpar sig för alla tillfällen. Anta att du vill lagra siffror i en tabell. En double kanske lämpar sig bäst för valutor:

1$number = R::dispense('number');
2$number->value = 199.99;
3R::store($number);
1$number = R::dispense('number');
2$number->value = 199.99;
3R::store($number);

Redbean ser till så att det blir just datatypen double för fältet. Men anta att man "råkar" försöka lagra ett par bokstäver i samma tabell:

1$number = R::dispense('number');
2$number->value = 'hej';
3R::store($number);
1$number = R::dispense('number');
2$number->value = 'hej';
3R::store($number);

Nu blir fältet till varchar istället. Kanske inte alltid optimalt.

När man utvecklar med Redbean rekomenderar jag starkt att man ofta använder R::nuke(); (gissa vad den gör..?) för att hålla strukturen optimal, för att därefter när man är nöjd med sitt schema använda sig av R::freeze(true); vilket fryser strukturen på databasen.

En annan sak att tänka på är att Redbean alltid kommer att vilja ha namnet id som primary key. Tidigare versioner erbjöd sätt att komma runt det (< 3.0) för att bättre stödja schemas som inte skapats via ORM:et, men plockades bort för att göra Redbean snabbare och mer effektivt. Så förvänta dig en del meckande om du vill använda Redbean med en tidigare databas som kör på sina egna standarder.

Källkoden för den här artikeln finns på GitHub.

Vad tycker du om Redbean? Ett bra eller dåligt sätt att utveckla på? Synpunkter på artikeln? Du hittar mig på Twitter; @olssonm.