第二十一天 - 一个红色的圣诞老人

A Red Secret Santa

这一年即将结束,我们有很多值得庆祝的事情!与家人和朋友相比,庆祝今年年底更好的方式是什么?为了帮助实现这一目标,在我家,我们决定开办秘密圣诞老人游戏!所以,我的目标是写一个秘密圣诞老人计划!这就是我可以使用这个名为Red的精彩项目的地方。

Red是一个仍在开发中的rakuORM (对象关系模型),尚未作为模块发布。但它正在增长,而且接近发布。

因此,让我们创建我们的第一张桌子:一张桌子,用于存储参与我们的秘密圣诞老人的人。代码:

use Red;

model Person {
   has UInt     $.id        is serial;
   has Str      $.name      is column;
   has Str      $.email     is column{ :nullable };
}

my $*RED-DB = database "SQLite";

Person.^create-table;

Person.^create: :name<Fernando>,    :email<fco@aco.com>;
Person.^create: :name<Aline>,       :email<aja@aco.com>;
Person.^create: :name<Fernanda>;
Person.^create: :name<Sophia>;

.say for Person.^all.grep(*.email.defined).map: *.name;

Red关系数据库映射 到OOP。每个表都映射到一个 Red类(模型),每个表的 对象代表一行

我们创建模型的方式是使用模型特殊单词。一个模型仅仅是延伸的正常类红::型号 ,具有MetamodelX ::红::型号的对象作为它的 元类Red不会向您的模型添加任何未明确创建的方法。因此,要与数据库进行交互,您应该使用元类

但是让我们继续吧。

代码创建一个名为 Person的新模型。此模型表示的的名称将与模型名称相同:“Person”。如有必要,您可以使用特征更改表的名称 (例如 :)is table<...> model Person is table<another_name> {...}

模型有3个属性

  • $ .name有一个 is column 特征 ;
  • $ .emailis column{ :nullable };
  • 和**$ .id**有一个 is serial。这意味着同样的is column{ :id, :auto-increment }

Red默认 使用非空列,因此如果要创建可以为空的列,则应使用 is column{ :nullable }

因此Person上的所有属性都是。在is serial(我指的是 :id 一部分)意味着它是表的主键。

之后,它为结果设置动态变量$*RED-DBdatabase "SQLite"。该数据库 收到司机的名字和它期望的参数。

在这种情况下,它使用SQLite 驱动程序,如果您不传递任何参数,它将使用它作为 内存 数据库。如果要使用名为secret-santa.db 的文件作为数据库文件,则可以执行此操作database "SQLite", :database<secret-santa.db>。或者,如果您想使用本地Postgres,只需使用 database "Pg"Red 使用变量 $*RED-DB 来知道要使用的数据库。

好的,现在让我们创建!正如我之前所说,没有添加任何方法你没有明确要求。因此,要创建表,使用元类 ' 方法Person.^create-table是你如何创建

这将运行:

CREATE TABLE person(
    id integer NOT NULL primary key AUTOINCREMENT,
    name varchar(255) NOT NULL,
    email varchar(255) NULL
)

现在我们应该插入一些数据。我们用另一个meta方法.^create)来做到这一点。该 .^create 元方法预期相同参数 .new 的期望。每个命名参数都将设置一个具有相同名称的 属性.^create将创建一个新的Person对象,将其保存在 数据库中(with .^save: :insert),然后返回它。

它运行:

INSERT INTO person(
    email,
    name
) VALUES(
    'fco@aco.com',
    'Fernando'
)

每个模型都有一个ResultSeq。这是代表每一个序列。我们可以 用(或)得到它的ResultSeqResultSeq有一些方法可以帮助您从表中获取信息,例如: 将过滤(就像在普通Seq中一样),但它不会在内存中执行此操作,它会返回带有该过滤器集的新 ResultSeq。检索其迭代器时,它使用ResultSeq上设置的所有内容运行SQL查询 。.^all``.^rs``.grep

在我们的示例中,Person.^all.grep(*.email.defined).map: *.name将运行如下查询:

SELECT
    person.name
FROM
    person
WHERE
    email IS NOT NULL

它会打印:

Fernando
Aline

好的,我们有一个代码可以保存谁进入我们的秘密圣诞老人游戏。但每个人都想要不同的礼物。我们怎么知道每个人的意愿?

让我们修改代码,使其为参与秘密圣诞老人的每个人保存心愿单:

use Red;

model Person { ... }

model Wishlist {
    has UInt    $!id        is serial;
    has UInt    $!wisher-id is referencing{ Person.id };
    has Person  $.wisher    is relationship{ .wisher-id };
    has Str:D   $.name      is column is required;
    has Str     $.link      is column;
}

model Person is rw {
   has UInt     $.id        is serial;
   has Str      $.name      is column;
   has Str      $.email     is column;
   has Wishlist @.wishes    is relationship{ .wisher-id }
}

my $*RED-DB = database "SQLite";

Wishlist.^create-table;
Person.^create-table;

my \fernando = Person.^create: :name<Fernando>, :email<fco@aco.com>;
fernando.wishes.create: :name<Comma>,          :link<https://commaide.com>;
fernando.wishes.create: :name("raku books"),  :link<https://rakubook.com>;
fernando.wishes.create: :name("mac book pro"), :link<https://www.apple.com/shop/buy-mac/macbook-pro/15-inch-space-gray-2.6ghz-6-core-512gb#>;

my \aline = Person.^create: :name<Aline>, :email<aja@aco.com>;
aline.wishes.create: :name("a new closet"), :link<https://i.pinimg.com/474x/02/05/93/020593b34c205792a6a7fd7191333fc6--wardrobe-behind-bed-false-wall-wardrobe.jpg>;

my \fernanda = Person.^create: :name<Fernanda>, :email<faco@aco.com>;
fernanda.wishes.create: :name("mimikyu plush"), :link<https://www.pokemoncenter.com/mimikyu-poké-plush-%28standard-size%29---10-701-02831>;
fernanda.wishes.create: :name("camelia plush"), :link<https://farm9.static.flickr.com/8432/28947786492_80056225f3_b.jpg>;

my \sophia = Person.^create: :name<Sophia>, :email<saco@aco.com>;
sophia.wishes.create: :name("baby alive"), :link<https://www.target.com/p/baby-alive-face-paint-fairy-brunette/-/A-51304817>;

say "\n{ .name }\n{ .wishes.map({" { .name } => { .link }" }).join("\n").indent: 3 }" for Person.^all

它打印:

Fernando
    Comma => https://commaide.com
    raku books => https://rakubook.com
    mac book pro => https://www.apple.com/shop/buy-mac/macbook-pro/15-inch-space-gray-2.6ghz-6-core-512gb#

Aline
    a new closet => https://i.pinimg.com/474x/02/05/93/020593b34c205792a6a7fd7191333fc6--wardrobe-behind-bed-false-wall-wardrobe.jpg

Fernanda
    mimikyu plush => https://www.pokemoncenter.com/mimikyu-poké-plush-%28standard-size%29---10-701-02831
    camelia plush => https://farm9.static.flickr.com/8432/28947786492_80056225f3_b.jpg

Sophia
    baby alive => https://www.target.com/p/baby-alive-face-paint-fairy-brunette/-/A-51304817

现在我们有一个新的 模型 愿望清单 ,它引用了一个名为withlist的表 。它 $!id 作为 ID$!name$!link 为列,也有一些新的东西! has UInt $!wisher-id is referencing{ Person.id }; 是一样 has UInt $!wisher-id is column{ :references{ Person.id } }; ,这意味着它是一个,这是一个 外键 引用 ID 的人。它也有 has Person $.wisher is relationship{ .wisher-id };不是一个,这是一个“虚拟”。在 $ 印记 意味着有 只有1好心人˚F 愿望。并 is relationship 期待一个 Callable 这将获得一个 模型。如果它是 标量 ,它将接收当前 模型 作为唯一参数。所以,在这种情况下,它将是 愿望清单。该relationsip的回报 可赎回 必须是引用其他一些

让我们看看这个表是如何创建的:

CREATE TABLE wishlist(
   id integer NOT NULL primary key,
   name varchar(255) NOT NULL,
   link varchar(255) NULL,
   wisher_id integer NULL references person(id)
)

如您所见,没有 创建wisher列。

模式 也发生了变化!现在它有一个 @.wishes 关系has Wishlist @.wishes is relationship{ .wisher-id })。它使用 @ sigil, 因此每个 都可以拥有多个愿望。 传递的 Callable将接收Positional 属性的类型 ( 在此情况下为Wishlist),并且必须返回引用其他列的列。

创建的表与以前相同。

我们之前创建了一个新的 Personmy \fernando = Person.^create: :name<Fernando>, :email<fco@aco.com>; 现在我们可以使用关系愿望)来创建一个新的愿望()。这为Fernando运行以下SQL创建了一个新的愿望:fernando.wishes.create: :name<Comma>, :link<https://commaide.com&gt;

INSERT INTO wishlist(
   name,
   link,
   wisher_id
) VALUES(
   'Comma',
   'https://commaide.com',
   1
)

你看过了吗? wisher_id1 … 1是费尔南多的身份。一旦你创建了Fernando的**.wishes()**的愿望 ,它已经知道它属于Fernando。

然后我们为我们创造的每个人定义愿望。

然后我们遍历 数据库中的每个 PersonPerson.^all)并打印其名称并循环该人的意愿并打印其名称和链接。

哦,我们可以拯救谁参与……得到他们想要的东西……但是平局?我应该送谁礼物?为此,我们再次更改程序:

use lib <lib>;
use Red;

model Person { ... }

model Wishlist {
    has UInt    $!id        is id;
    has UInt    $!wisher-id is referencing{ Person.id };
    has Person  $.wisher    is relationship{ .wisher-id };
    has Str:D   $.name      is column is required;
    has Str     $.link      is column;
}

model Person is rw {
   has UInt     $.id        is id;
   has Str      $.name      is column;
   has Str      $.email     is column;
   has UInt     $!pair-id   is referencing{ ::?CLASS.^alias.id };
   has ::?CLASS $.pair      is relationship{ .pair-id };
   has Wishlist @.wishes    is relationship{ .wisher-id }

   method draw(::?CLASS:U:) {
      my @people = self.^all.pick: *;
      for flat @people.rotor: 2 => -1 -> $p1, $p2 {
         $p1.pair = $p2;
         $p1.^save;
      }
      given @people.tail {
         .pair = @people.head;
         .^save
      }
   }
}

my $*RED-DB = database "SQLite";

Wishlist.^create-table;
Person.^create-table;

my \fernando = Person.^create: :name<Fernando>, :email<fco@aco.com>;
fernando.wishes.create: :name<Comma>,            :link<https://commaide.com>;
fernando.wishes.create: :name("raku books"),    :link<https://rakubook.com>;
fernando.wishes.create: :name("mac book pro"),   :link<https://www.apple.com/shop/buy-mac/macbook-pro/15-inch-space-gray-2.6ghz-6-core-512gb#>;

my \aline = Person.^create: :name<Aline>, :email<aja@aco.com>;
aline.wishes.create: :name("a new closet"), :link<https://i.pinimg.com/474x/02/05/93/020593b34c205792a6a7fd7191333fc6--wardrobe-behind-bed-false-wall-wardrobe.jpg>;

my \fernanda = Person.^create: :name<Fernanda>, :email<faco@aco.com>;
fernanda.wishes.create: :name("mimikyu plush"), :link<https://www.pokemoncenter.com/mimikyu-poké-plush-%28standard-size%29---10-701-02831>;
fernanda.wishes.create: :name("camelia plush"), :link<https://farm9.static.flickr.com/8432/28947786492_80056225f3_b.jpg>;

my \sophia = Person.^create: :name<Sophia>,   :email<saco@aco.com>;
sophia.wishes.create: :name("baby alive"),      :link<https://www.target.com/p/baby-alive-face-paint-fairy-brunette/-/A-51304817>;

Person.draw;

say "{ .name } -> { .pair.name }\n\tWishlist: { .pair.wishes.map(*.name).join: ", " }" for Person.^all

现在Person 有两个新属性$!pair-id和**$ .pair**)和一个新方法(**draw**)。 **$!pair-id** 是一个 引用 同一个*表* (**Person**)上 的字段**id**的*外键*,因此我们必须使用 *别名* ()。另一个是使用该*外键*的*关系* (**$ .pair**)。.^alias

新方法(平局)是神奇发生的地方。它使用方法 .pick:* 在普通的 Positional 上将洗牌。它在这里做同样的事情,查询:

SELECT
   person.email , person.id , person.name , person.pair_id as "pair-id"
FROM
   person
ORDER BY
   random()

一旦我们有了洗牌列表,我们就会使用 .rotor 来获取两个项目并返回一个,所以我们保存每个人给予下一个人的那一对,并且列表中的最后一个人将给第一个人。

这是我们最终代码的输出:

Fernando -> Sophia
	Wishlist: baby alive
Aline -> Fernanda
	Wishlist: mimikyu plush, camelia plush
Fernanda -> Fernando
	Wishlist: COMMA, raku books, mac book pro
Sophia -> Aline
	Wishlist: a new closet

作为奖励,让我们看一下Red将要跟随的曲目。这是当前的工作代码:

use Red;

model Person {
   has UInt     $.id        is id;
   has Str      $.name      is column;
   has Str      $.email     is column{ :nullable };
}

my $*RED-DB = database "SQLite";

Person.^create-table;

Person.^create: :name<Fernando>,    :email<fco@aco.com>;
Person.^create: :name<Aline>,       :email<aja@aco.com>;
Person.^create: :name<Fernanda>;
Person.^create: :name<Sophia>;

.say for Person.^all.map: { "{ .name }{ " => { .email }" if .email }" };

这是它运行的SQL:

SELECT
   CASE 
      WHEN (email == '' OR email IS NULL) THEN name
   ELSE name || ' => ' || email
   END
    as "data"
FROM
   person

它打印

Fernando => fco@aco.com 
Aline => aja@aco.com 
Fernanda 
Sophia

comments powered by Disqus