Liza Shulyayeva

Pathfinder: Two Heroes Are Born

It was a chilly summer evening when Valeros the valiant fighter and Merisiel the super cool rogue walked through the gates of a tiny abandoned farmhouse. They were heroes, you see, and here to rescue the people of the land from a gang of notorious thieving wanderers - the Sczarni - who have been terrorizing Sandpoint under the leadership of a punk named Jubrayl Vhiski.

When the townsfolk put out an ad for two brave heroes to take care of the problem Valeros and Merisiel felt it was unnecessary to mention their lack of experience in adventuring. But now the creepy creaking gate and the menacing-sounding wind was making Merisiel second-guess their decision.

But they pressed on. Merisiel had her dagger and darts at the ready and Valeros had his…some other weapon…also at the ready as they tiptoed down the path to the house. Suddenly, a Mercenary jumped out of the nearby apple tree and kicked Merisiel to the ground! She recovered and threw a dart at his face. It missed, so she threw it away and tried again. Got him! Merisiel kept his eye as a souvenir - and his elite throwing axe. Unfortunately she wasn’t that great with proper weapons, so as she practice-swung the axe around in victory it nicked the side of her thigh. Worth it, though.

Valeros was the image of heroism as he, emboldened by their defeat of the Mercenary, kicked open the door of the farmhouse and strolled inside. That’s when the siren leaped out at his face from the rafters. He slashed it with his short sword, but she swiped one wing into his head and Valeros dropped to the floor, dead.

Merisiel was alone. “I will bring you back, Valeros!” she yelled as the ran away from the farmhouse very quickly. She ran to exhaustion and found herself at a wooden bridge, where a henchman waiting for her! She threw the axe at it, dislocating her shoulder in the process. But it worked. The henchman perished and she secured the entire location. She fought monsters across the land, hoping to stumble into anything that could bring Valeros back to life. She journeyed from the bridge to the waterfront and then to the woods with no luck. Close to death, she managed to barely escape a Giant Gecko when suddenly she realized…He’s not dead! He’s just unconscious!

She ran over to the farmhouse to wake up Valeros, who was very glad to find out that he wasn’t dead after all and just napping for seven hours or so while Merisiel was beating up monsters and henchmen on his behalf with her massive axe. Together they stole a couple of farm horses and rode over to the waterfront, where Jubrayl Vhiski himself was waiting for them! Valeros, well rested from his long nap, took on the gang leader. Unfortunately, he got away. The gang leader, not Valeros. However, at least they got to secure another location.

“We’ve got to split up,” said Merisiel. “We have two locations left - the farmhouse and the woods. If one of us runs into Vhiski again, the other can secure the remaining location.”

It was decided that Merisiel would take the woods (since she’s an elf and all) and Valeros would go back and finish clearing the farmhouse. Vhiski had to be hiding in one of those places.

Except it just so happened that the siren wasn’t done with Valeros. She lunged at him again when he arrived at the house. Only wisdom could defeat her and neither Merisiel nor Valeros had much of that going for them (though let it be known that Merisiel had a little more). Merisiel helped Valeros escape from afar, but the siren herself was not defeated. She was still out there…hiding.

“No way, man.” said Valeros. “I’m not fighting that thing again. She’ll kill me!”

“Fine, let’s trade places.” Merisiel relocated to the farmhouse and Valeros took over the forest. Suddenly, a bandit henchman (or something) leapt out at Merisiel from the tall grass of a pasture. She threw a dagger through his heart and then, as a result, managed to close the location. Yet as she was boarding up the doors and windows of the farmhouse she spotted Vhiski himself hiding in a corner. She decided not to let her presence be known just yet and communicated with Valeros (who was in the woods) via carrier pigeon.

“Enemy spotted. He’s strong. I think I can take him, but you may be better with your superhuman strength.” she wrote.

They did some quick math and confirmed that Valeros, with his mighty strength and stuff, did indeed have a better chance of defeating the villain. Merisiel could provide some healing assistance if required. They formulated their cunning plan. It went like this:

Merisiel and Valeros quietly switched locations again - Merisiel retreating to the woods and Valeros sneaking into the farmhouse. Valeros pounced out at Jubrayl Vhiski, charging at him with all his might. A very fast carrier pigeon sent word to Merisiel that the battle was beginning. She, in the meantime, was trying to figure out how to temporarily close the woods to ensure Jubrayl couldn’t escape there when Valeros defeated them.

“But oh no!” she exclaimed. “It requires wisdom! We are toast. Even if Valeros manages to defeat Jubrayl now, he’ll just escape into the woods and we’ll have to wade through all these hostile creatures. We’ll be dead before we get to him!”

They should probably have taken the time to figure this out before putting their plan in action, but as we’ve stablished - not much wisdom on either end.

Pathfinder: wisdom roll Merisiel had one shot at solving a seemingly impossible puzzle. All she could do was guess. Out of six choices, only one was correct. There was zero room for error. Sweating profusely, she guessed…”Six!

The monsters of the forest cried out in horror and retreated to their dens. The forest closed in on itself. Success!

In the meantime, Valeros was fighting an epic battle with a gang leader who was beating him with a club.

“You can do it, Valeros!”

And he did! With a final good kick to the head, Jubrayl was knocked unconscious (permanently). Valeros looked to the sky at just the right angle for sunlight to shine on his chiseled features.

And so, Sandpoint was freed from the clutches of an evil villain.

“We did it!” Merisiel punched the air victoriously, nearly stabbing Valeros in the face with her dagger. “Let’s do that again!”

They started dividing their loot, preparing for their next (big) adventure.

It was decided that Merisiel should probably hand over the axe to Valeros, who was able to use it without injuring himself. Valeros then passed out, clearly requiring another nap (seriously, he’s snoring right now).

Wolfling Runs

Wolfling run: “a method of playing the Creatures games in which the players do not intervene in the lives of their norns.” Creatures wiki.

Here is one of the original detailed explanations - Wolfling Run FAQ

I’ve mentioned implementing a way to do wolfling runs in Gastropoda a few times, but then realized that it’s not exactly a commonly known term (at least for people who’ve never played Creatures). I only recently learned that the term is actually based on David Brin’s Uplift novels.

Most of humanity believes itself to be a wolfling species that emerged into sapiency solely through natural evolution, without genetic manipulation of a patron species. Uplift Universe wiki

There are different kinds of wolfling runs, but basically when I played Creatures 3 I mostly ran relaxed wolfling runs. I’d set up the world, then leave my norns for a few hours to breed on their own. It was always really interesting to see which ‘lines’ survived. I even made up stories for them, documenting each norn’s life and breeding patterns in a notebook. At the end of the wolfling run I’d crown an alpha male and female - those who spread their genes the most during the run.

Anyway, I want to set up something similar for the snails. I haven’t yet decided if this will be the default state for the breeding jars…I’ll either just have a Wolfling Run checkbox (or item) that makes the jar disregard capacity and let the snails breed wild. At the moment, breeding events are only triggered when you move a snail into the breeding jar. In reality, I suppose snails would periodically try to mate whenever they’re in the breeding jar. I’d have to get the balance right, make it realistic…they can’t just be breeding like rabbits. The other alternative is requiring the user to get a really high capacity breeding jar. These would be very expensive in terms of the virtual currency, but would be perfect for wolfling runs.

I might start on this now and then move on to race actions.

On Snail Eggs

Even though I’ve had a functional breeding system in Gastropoda for a while, it still isn’t actually done. Most common snails do not give birth to live young…and definitely not to one live young. Initially it was good to just have the snails pop out one live baby snail so that I could properly observe and test the genetics and other attributes generated for the newborn. Now, however, with that being well on track, it is time to implement…egg laying.

The requirements were this:

  • Quantity of eggs is based in part on the snail’s fertility attribute. Environmental and health factors will eventually come into effect.
  • The snail now has to be pregnant for some time before actually laying the eggs. Eggs have to stay in the jar for some time before they hatch into baby snails.
  • The baby snails are pregenerated at the time of the mating, not on egg hatching. This is because eventually environmental factors and the parents’ health may have some effect here as well. I can easily modify additional attributes before hatching if needed, but the main attributes of the newborn snails must be set based on the state the parents were in when they actually mated.
  • Snails can’t be born at their full size - they have to be born tiny and grow based on a maturity rate attribute.

At first I considered having a separate table for eggs, but then I decided not to complicate things. I added an eggLaid attribute to the snail table. By default eggLaid is set to false, so at the time of conception the snail-to-be exists but it doesn’t appear as an egg. The birthDate is NULL at this time. In the jar we can see the eggs displayed on top of the pregnant snail’s shell like so:

Pregnant snail

(The number of circles on the shell doesn’t represent how many eggs will actually be laid. In the future I’ll make this much more subtle, the quantity will be indistinguishable).

A LayEggs event is created in the db. Currently it’s always scheduled for exactly an hour after mating. When this event runs eggLaid is set to true in the snails table for those snails and they now appear as eggs (you can see an example of one of the eggs in the jar above). The color of the egg represents (to an extent) the shell color of the newborn, though without the pattern overlay it’s impossible to tell what the final snail will look like at this time. For me that’s pretty exciting, because I can come back in a few hours to start seeing what kind of shell pattern and eye color my baby snail is developing.

When the eggs are laid, a HatchEggs event is created. Eggs will hatch roughly 24 hours after they’re laid, but this will eventually have a tiny degree of randomization as well as be affected by factors like temperature and subtrate within the jar.

When the eggs are hatched, the snail is so tiny that you can barely see it. From there every hour the scale of the snail is increased based on the maturity rate attribute. Eventually the snail grows to its full size!

Up next I have to:

  • Make sure that snails under 3 days of age do not count toward the jar capacity quota
  • Make sure that snails can’t be picked for breeding until they’re fully mature
  • Implement aggression - baby snails are vulnerable and can be eaten by other snails

New Snail Shell Patterns

This post originally started out like this:

I’ve made a few more shell patterns for the snails!

I then started to descibe how the basic genetics work in the simulation (limited to pattern color). While doing so, I began to realize that it is all wrong. I went back and ripped apart some of the breeding system to get it to a slightly more realistic state. Needless to say it’s still not really correct, but at least it’s a bit closer to what could be some sort of reality. Now I have to go back and clean everything up/rewrite it.

I’ll keep the explanation to pattern color, but there is also pattern shape, shell color, eye color, and shell radius genetic traits (and more background genetic traits that don’t actually translate to anything visually on the snail).

(Sidenote: I’m already noticing bugs with this as I type this…and filing them on the Trello board…this obviously has a long way to go)

For randomly generated wild snails

When generating a random snail, random integers between 0 and 255 are chosen for the pattern color.

    $this->patternColorR = Utility::randomInteger(0,255);
    $this->patternColorG = Utility::randomInteger(0,255);
    $this->patternColorB = Utility::randomInteger(0,255);

When this and the other genetic traits have been randomly generated, generateGeneString() is run.

    $arrayOfColors = [
        'r' => $this->patternColorR, 
        'g' => $this->patternColorG, 
        'b' => $this->patternColorB
    $this->patternColorGeneAllele1 = strtoupper($this->findDominantColor($arrayOfColors));
    $this->patternColorGeneAllele2 = $this->pickRandomRecessiveColorAllele($this->patternColorGeneAllele1);

Why convert the allele to upper case instead of just naming the keys in upper case? I preferred to do it this way because I want it to be clear that the keys in $arrayOfColors do not represent alleles. These refer to color amounts themselves. Alleles are in upper case.

So, to get the first allele in the randomly generated snails we find the dominant color in arrayOfColors and convert it to an allele (in upper case): $this->patternColorGeneAllele1 = strtoupper($this->findDominantColor($arrayOfColors));

When we know the first allele, we need to generate a second one (which the first is dominant over) to complete the desired genotype via $this->patternColorGeneAllele2 = $this->pickRandomRecessiveColorAllele($this->patternColorGeneAllele1);

In pickRandomRecessiveColorAllele there is this:

        $possibleAllelesArray = [];
        switch (true) {
            case ($dominantAllele === 'R'):
                $possibleAllelesArray = ['R','G','B'];
            case ($dominantAllele === 'G'):
                $possibleAllelesArray = ['G','B'];
            case ($dominantAllele === 'B'):
                $possibleAllelesArray = ['B'];
        $allele = Utility::randomInteger(0,count($possibleAllelesArray)-1);
        return $possibleAllelesArray[$allele];
  • Red is always dominant over Green and Blue, so if there is one R allele it doesn’t matter what the second one is.
  • Green is dominant over Blue, so if the dominant allele should be G the only possible recessive alleles are G and B
  • Blue is always recessive, so if we want B to be the dominant allele the only possible second allele is B.

For newborn snails with two parents

When generating a newborn snail and not just a random snail the process is kind of reversed. We get the alleles first, then the colors themselves:

    $this->patternColorGeneAllele1 = $this->generateAlleles($stag->patternColorGeneAllele1, $stag->patternColorGeneAllele2);
    $this->patternColorGeneAllele2 = $this->generateAlleles($doe->patternColorGeneAllele1, $doe->patternColorGeneAllele2);
    protected function generateAlleles($traitAllele1, $traitAllele2) {
        // Make an array of the two possibilities
        $possibilities = [$traitAllele1, $traitAllele2]; 
        // Pick one of the elements in the array at random
        $rand = Utility::randomInteger(0,1);
        return $possibilities[$rand];

We then make an array of each of the parent snails’ pattern colors and run $patternColor = $this->generateVisibleTraits('patternColor', $stagPatternColor, $doePatternColor);

In generateVisibleTraits() we get the alleles for the trait in the form of an array.

    $alleles = $this->getAllele($trait); // Array (2)
    if ($trait !== 'patternShape' && $trait !== 'patternRadius') {
        $a1 = strtoupper($alleles['allele1']);
        $a2 = strtoupper($alleles['allele2']);
        switch (true) {
            // If both alleles are identical use both stag and doe traits 
            case ($a1 === 'R' && $a2 === 'R') || ($a1 === 'G' && $a2 === 'G') || ($a1 === 'B' && $a2 === 'B'):
                return $this->pickColor($a1, $stagTrait, $doeTrait);

            // If either allele is R (for red), set it as the color - Red is always dominant
            case $a1 === 'R':
                return $this->pickColor('R', $stagTrait);
            case $a2 === 'R':
                return $this->pickColor('R', $doeTrait);
            // Else if either allele is G (for green), set it as the color - Green is second dominant
            case $a1 === 'G':
                return $this->pickColor('G', $stagTrait);
            case $a2 === 'G':
                return $this->pickColor('G', $doeTrait);
            // Else if either allele is B (for blue), set it as the color - Blue is always submissive
            case $a1 === 'B':
                return $this->pickColor('B', $stagTrait);
            case $a2 === 'B':
                return $this->pickColor('B', $doeTrait);

In pickColor() you can optionally pass up to two parentTrait arguments. In some cases you don’t need to, but for patternColor there should always be at least one. If there are two, we assume incomplete dominance and return a mix of both parents’ dominant color.

    protected function pickColor($allele, $parentTrait1 = null, $parentTrait2 = null) {
        // RGB min & max values
        $minValue = 0;
        $maxValue = 255;
        $posKeys = ['R','G','B'];
        $color = strtolower($posKeys[array_search($allele, $posKeys)]);

        if (isset($parentTrait1) && isset($parentTrait2)) {
            $minValue = $parentTrait2[$color];
            $maxValue = $parentTrait1[$color];
        else if (isset($parentTrait1)) {
            $minValue = $parentTrait1[$color] - 15;
            $maxValue = $parentTrait1[$color] + 15;

        $randomColorAttributes = "";

        foreach ($posKeys as $key) {
            if ($key === $allele) {
                $min = $minValue;
                $max = $maxValue;
            else {
                $min = 0;
                $max = $minValue;
            $randomColorAttributes .= $min . ',' . $max;
            if ($key !== 'B') {
                $randomColorAttributes .= ',';
        $randColor = call_user_func_array("Utility::randomColor", str_getcsv($randomColorAttributes));
        return $randColor;

Breeding example

Here’s an example of a breeding. In the simulation I refer to a snail that bred as a male as a “stag”, female as “doe”, baby snail as “foal”.

In this case Ice (#327) and Fire (#329) were placed in the breeding jar. They were the only two snails in the jar to make sure they bred with each other.

Stag: Ice (#327)

Ice snail
Bred as a male
Shell color genotype: GB
Eye color genotype: RB
Pattern color genotype: BB
Pattern shape genotype: FE
Pattern radius genotype BS

Doe: Fire (#329)

Fire snail
Bred as a female
Shell color genotype: RG
Eye color genotype: GB
Pattern color genotype: RR
Pattern shape genotype: EE
Pattern radius genotype BS

Foal: Unnamed (#330)

Foal snail
Shell color genotype: BG (G > B)
Eye color genotype: RG (R > G)
Pattern color genotype: BR (R > B)
Pattern shape genotype: EE
Pattern radius genotype SS

But the new pattern shapes!

Here are some screenshots of various pattern shape/color combinations in the simulation today. These are still placeholders and will change. I don’t like all the super obvious tacky gradients. But it kind of gives me more variety to work with. There are currently 6 pattern options (one of which is lack of pattern)

Snail patternSnail patternSnail patternSnail patternSnail patternSnail patternSnail patternSnail patternSnail patternSnail patternSnail patternSnail patternSnail patternSnail patternSnail patternSnail patternSnail patternSnail pattern

Laravel Migration Done

The first commit auto-tweeted from the @gastropoda_ twitter account yesterday was actually the last commit dedicated to the Laravel port reimplementation:

Yeah, that one. It contained basically some racing fixes and the reimplementation of recurring events (snail idle actions, store restocking, nightly snail energy depletion), the corner store, and user inventory.

Laravel - you have arrivedI started the port on July 10, so accounting for 2 days away in Amsterdam where no snail work got done, overall it took 16 days to migrate everything to Laravel (between about 1-4 hours of work per day).

From now on any further work on Gastropoda will move past the functionality of the vanilla PHP version. And I’ve already started! I’ve implemented rudimentary race logs and action templates (right now the only action template is ‘move’, but there will be lots of other actions a snail can perform during a race).

The Gastropoda board on Trello has been helping me keep track of my progress and it’s getting fuller by the day. If you haven’t checked out Trello yet, you should. Here is a non-referral link or, if you’re feeling nice, a referral link.

Up next I’ll be working on whatever the crap I want. More specifically, I might start with adding a couple more action templates for the races. Then I might switch to something totally different and look into generating better shell patterns and colors on the snails. The snails are actually displayed on HTML5 canvas (since they crawl), so all of that is done in JS/HTML5. I bet I can get some really cool and more natural looking visual effects in place of the placeholder patterns I have now. I also still need to capture a static snail image and store it in the DB for cases where a single snail avatar needs to be displayed as a simple image only.

A Battle With Racing

I’m still on the Laravel migration. It’s going pretty well, except for last night. Last night I was porting racing.

It was awful.

I didn’t get to bed until something like 4am and my brain seems to have blocked out a sizeable chunk of what actually happened from memory, but during the course of the afternoon I went through problem after problem. Each time I solved one thing some other functionality broke and by the end I didn’t know wtf was going on.

The problem started with my fundamental misunderstanding of the Eloquent update(). After reading the Laravel documentation I got the impression that update’s entire purpose is updating specific attributes of a model. You pass an array of attributes, Eloquent updates the table.

Apparently that’s not how it works. When you run Model::update($propertiesToUpdate) it doesn’t just update those properties - it looks for any dirtying of the model and apparently tries to save everything that may have changed. Unfortunately in my current setup the model also represents a complete instance of a snail that has additional properties set after it is loaded. So as an example…snail->currentEnergy is not stored in the database, but calculated when the model is loaded based on the snail’s current protein, fat, and carb counts. When I tried making neccessary changes to the racing snail post-race to record depletion of these macros I was getting an error about a column not existing. But it wasn’t supposed to exist at all.

I went to Laravel IRC for help. People were very helpful and responsive. It took a while of going in circles. I was getting a little frustrated with the whole situation because it cannot be that difficult to save a fricking change to some specific fricking fields and only those fields in the db through Eloquent. I would have thought it would be at least as simple a process as manually building a query and running mysqli was. In the end we decided that using the query builder was my best bet. So now instead of using $snail->update($propertiesToUpdate) I use DB::table('snails')->where('snailID', $this->snailID)->update($propertiesToUpdate)

This works. I do eventually want to go back and change my entire setup to avoid dirtying the model. For now, though, I just want to get this thing back to the state it was in the vanilla PHP version.

Anyway, then there were some other issues with Eloquent relationships, but those were caused by sleepiness more so than any actual bug or source of confusion. Here’s an example of how my Laravel relationships are set up for races and their entries at the moment:

  • Race model hasMany entries
  • RaceEntry model hasOne snail

This makes it easy to immediately load the entries associated with each race as well as the snails associated with each entry for use during the race. In a similar way, I can easily get a snail’s owner via a belongsTo relationship. From there it’s just a matter of Eager loading the model associated with the relationship when loading the parent model itself. Or lazy loading it via $race->load('entries') for example.

Anyway, the races are running now. They’re not done, but they’re now up to the same state as the vanilla PHP version. Up next I want to do a bit of cleanup and then start reimplementing items. This port is taking longer than I thought, but I can see the benefits of using Laravel. I’m finding myself cutting out huge chunks of code I had in the vanilla version that, with the help of Laravel and Eloquent, are just no longer needed. Eloquent makes data retrieval and validation much easier and faster to implement.

Up next I want to do some cleanup and then start on the item reimplementation. Wish me luck.

Setting Up an AWS EC2 Instance for Gastropoda

It all started with a cool little PHP script that someone posted on /r/gamedev this morning to publish completed Trello tasks easily to a change log of sorts.

I had just re-started using Trello, creating a public Gastropoda board to keep track of my progress (starting with the migration to Laravel).

So I was excited to try out the script and have some sort of automated progress log. What I conveniently forgot was that my Octopress host - AWS S3 - only hosts static sites. PHP is out of the question. And I figured…I could probably generate the actual page and publish it as a static site just like I do with Octopress, but I’m going to need to host Gastropoda somewhere anyway and I wanted the whole Trello-link thing to be real time. I may as well set up the whole shebang now.

Well, I didn’t end up setting up the whole shebang because I didn’t want to pay for RDS while only hosting one PHP file. I did, however, set up an EC2 instance through Elastic Beanstalk and point a subdomain to it. You can now find a simple progress log (taken from my Trello “Done” column) at

My worry with this thing is how much it’ll cost. S3 instance is ridiculously cheap, but it seems as soon as you migrate to the world of ec2 and databases the costs go up substantially. If you just want to host a static site, S3 is way cheaper than a regular shared host. However EC2 with RDS can be considerably more expensive than a shared host (although the service is of course better). We’ll see what the first bill is for this one simple low-traffic PHP page. The setup looks like this:

  • Set up through Elastic Beanstalk
  • One application with so far one environment
  • t2.micro (so with VPC)
  • No RDS (yet, though I’ll obviously need it when it comes time to host the actual simulation)
  • Continuing to use Route 53 for domain management
  • Hosting actual site files on S3 (of course as it’s auto-uploaded to the designated bucket during deployment)

There are some bugs with the script (like labels having to be applied in a certain order if there are multiples on one task), which I might take the time to fix myself later. For now though it works.

In the meantime, I’m now porting the racing implementation from vanilla PHP to Laravel. I’d say I’m about 75% of the way there. Slowly but surely.

Summer Holidays and Amsterdam

July is when Sweden goes on holidays. Businesses are closed for the summer (quite a few of them, anyway), people take time off work, the weather doesn’t suck. Usually people tend to go away to other countries, which to me is ludicrous because after all that snow and cold these months are finally when Sweden gets warm and sunny. I guess I’m kind of fibbing - I get it. You want to get out and see other places during your vacation.

So we went to Amsterdam for two days this month. It was great, but too short. On the last day we almost “forgot” that we had a plane to catch that night and considered maybe just “accidentally missing it”. It’d be an adventure, that’s for sure. But as the day went on I actually got excited about coming back home to Sweden. Amsterdam is great to visit, but there’s nothing there that makes me feel like I’d rather be there than in Stockholm. One big thing I did take away from it though was the desire to bike far more regularly instead of catching the train. I think since we got back we haven’t gotten on a train to Södermalm once, opting to cycle instead.

We didn’t take our laptops and put heavy restrictions on internet use. Mostly we just wanted to disconnect and relax. It worked, although I kept thinking about snails, taking notes, etc.

When we got to the Amsterdam airport we saw the TVs in an electronics store talking about a flight and showing disturbing footage. Through a store clerk who translated for us we learned that flight MH17 had just been shot down by terrorists in Ukraine after leaving that same airport that same evening. We were in shock for a while, with no idea of what was happening. I finally got to wifi to be able to read some English and Russian-language news. I hope that the terrorists - and anyone sponsoring them - are brought to justice.

{{ ‘amsterdam’ | image_list }}

Custom Validators in Laravel

I’m still going with the Laravel port. One thing I’ve found really interesting is custom validators. In Laravel models you can set field rules, such as…

public static $rules = array(
    'name'                => 'required|alpha_spaces|unique:jars',
    'ownerID'             => 'integer',
    'isBreedingJar'       => 'boolean',
    'capacity'            => 'integer|max:15',
    'comment'             => 'between:0,32|alpha_spaces',

In the vanilla PHP version I had to perform a check in updateSnail() - if the modified field was currentJarID, I had to find the number of snails inside a jar with that same ID and compare the jar’s capacity to the count of those snails. If capacity <= current snail count, the snail was not allowed to be placed into that jar.

With Laravel, I hacked together the same thing in the form of a new validation rule. This way I don’t have to explicitly perform this check whenever I move a snail myself - I just set the rule name in the rules array for the Snail model and it does the rest whenever it sees the snail’s currentJarID being modified.

I plan on changing this when I can see better ways of doing this. For now, setting it up like this seems to work:

1) Create app/validators.php

2) Extend Validator with your own rule

Validator::extend('limited_jar_space', function($attribute, $value, $parameters)
    $jarID = Input::get('currentJarID');

    $jarController = new JarController();
    $jar = $jarController->getJarDetails($jarID, 'capacity');
    $snailController = new SnailController();
    $snailCount = count($snailController->findSnailsInJar($jarID));

    if ($jar->capacity <= $snailCount) {
        return false;
    return true;

3) Create a custom error message in lang/x/validation.php

"limited_jar_space"        => "The jar is full!",

4) Add app/validators.php to start/global.php

require app_path().'/validators.php';

5) So now I can use the rule "limited_jar_space" for currentJarID in the snail model to make sure the jar a user is trying to move their snail to has enough space left.

public static $rules = array(
    'name'                   => 'alpha_spaces|unique:snails',
    'ownerID'                => 'integer',
    'currentJarID'           => 'integer|limited_jar_space',

Experimenting With Laravel

I’m sitting here working on Gastropoda on a train to Norrköping. A couple of days ago I started experimenting with Laravel after the friendly folks on /r/php provided some invaluable input to my vanilla PHP vs framework question.

Laravel has been easy to set up. All in all getting Vagrant up and running and Laravel installed took maybe up to an hour. I’ve been trying to think of a good way to port my existing setup to an MVC-based framework. I’m basically going to paste a copy of what I posted on the forum to explain the work ahead.

Basically, in the vanilla PHP version I have pages, main object classes, and manager object classes. Two examples:

  • snail.php
  • snail.class.php
  • snailmanager.class.php

  • jar.php
  • jar.class.php
  • jarmanager.class.php

I should note that snail data is not limited to being displayed on snail.php - snails are also instantiated and displayed on jar.php, for example.

Currently, in the no-framework version, I’ve been structuring every main object class (ie snail.class.php) in a way that ensures the class only contains methods that pertain to a specific instance of an object. So you’d never see a method like findSnailsByJarID() in snail.class.php because it would often return more than one snail. Instead, those sorts of things - methods that require pulling out multiple snails based on given requirements or trigger any sort of events on a set of snails/jars/whatever would go into snailmanager.class.php, jarmanager.class.php, etc.

An example of stuff in snail.class.php would be: updateSnail, loadSnailInfo, generateNewbornSnail, generateGeneString, depleteEnergy, etc.

An example of stuff in snailmanager.class.php would be: findSnailsByJarID, nightlyEnergyDepletion (which would call depleteEnergy for each relevant snail), etc.

The question

How should I split this up in an MVC pattern as pertaining to Laravel? Where should the logic pertaining to each instance of an object go? Some people have suggested putting everything into the controller, others in the model. Most seem to suggest not separating methods that affect one instance of an object from methods that perform actions on multiple objects (so there would be no separation such as the one I had with snail.class.php and snailmanager.class.php).

Anyway, while I do more research I’ve kind of been hacking it together with the expectation of later changing it as I learn. So the way I have it set up now for a jar is:

  • /models/Jar.php
  • /controllers/JarController.php
  • /views/stable/jar.php

The data I need to pass to the view is retrieved via a View Composer, which communicates with JarController, SnailController, and UserController to get information about a jar’s owner and all the snails within it. In a sense my controllers in Laravel kind of take on the role of the managers in the vanilla PHP version, except when data needs to change the view would never talk directly to the model but go through a relevant controller. The view also no longer decides anything (eg, whether to show the Edit form for a jar if the user viewing a jar is its owner). This is all done before, in the view composer, and from there the edit form is displayed if it is available.

Anyway, I am going to have to change how this works a likely stupid amount of time as I go.