Why oh why won’t CakePHP store my tinyint(1) data?

CakePHPOK, I actually know the answer to the question posed in the subject line. Despite the palpable suspense, I am sure 99.9% of my audience can tune out now. That leaves the remaining 0.001 readers to dive into this problem with me.

I’m doing a lot of CakePHP development these days, and I’m loving it. (What? The McDonald’s legal team is on its way over here? Damn. Fine, McDonalds. Have it your way. What, Burger King too? I guess I better make a run for the border. OK, joke’s over.)

Of course, plunging into a sea of someone else’s code is always fraught with a little peril, and today I found some. Here’s the scenario:

MySQL is a pretty cool database. I’m very loyal to it. But there are some things it should do, but it just doesn’t. One of those things is support a boolean data type. So, we make do. A common way to make do, and the way preferred by CakePHP, is to use a tinyint(1) field and just store 0 or 1 in it. In fact, CakePHP loves this approach so much that whenever it sees a data field that’s a tinyint(1), it “automagically” refuses to accept any values for that field other than 0 or 1.

That’s super-dee-duper. If that’s what you want. But I have a data table, my users table, as it happens, and in that table I used to have a field called admin, a boolean value. Either the user’s an admin or not. Great. But I decided to upgrade this to allow more access levels than just 0 or 1 (and while I was at it, changing the field name, shockingly, to access_level). I wanted to be able to support up to ten levels. Well, great! A tinyint(1) will nicely store values from 0 through 9, so I’m set!

Except… it didn’t work. Every time I tried to save a value greater than 1, I’d find that it had saved 1 as the value. I could change it to 0 just fine, but anything else became 1.

I checked the documentation and found ample evidence for the “automagic” behavior, so I figured there were a couple of possible changes I could make that would fix the problem: I could change it to a tinyint(2), or I could change it to a char(1). Since I decided I’d rather (in theory) allow letters in the field than double-digit numbers, I went with char(1).

Only it still didn’t work.

I did some more research, found that I seemed to be on the right track, but I was still confounded.

Then it occurred to me. I knew I had seen a cache directory somewhere in the labyrinth of directories and subdirectories and sub-sub-sub-sub-subdirectories in the CakePHP package. And I also knew that a framework as developed as CakePHP probably wouldn’t hit up the database for schema information constantly, so maybe… just maybe… it was cached.

I burrowed down in my application to the app/tmp/cache/models directory, and sure enough… there’s a cache file for each data table, with the schema in a serialized form. Well, it’s a cache, right? Nothin’ to lose. Trash can, here we come! I refreshed my page, and voilà! Success!

So… word to the wise (or, not so wise, like me… otherwise you’d probably already know):

  1. CakePHP will only store 0 or 1 in a tinyint(1) field. Period.
  2. If you change the schema for any of your data tables, and CakePHP acts like you didn’t… dump that cache!

We now return you to our regular, slightly less geeky programming.

CakePHP Auth component, Flash and Internet Explorer… a deadly combination

OK, it's not really deadly at all... other than that it will kill your CakePHP session and log you out. My CakePHP-based CMS uses YUI Uploader, a Flash-based file uploader utility. It's much better... »
CakePHP paginator sorting problem solved!

I'll keep this brief, because I need to get back to writing code, but I wanted to share the solution I found to a CakePHP problem that has been nagging me for a while and for which I had never found a... »
CakePHP headaches

I'm in the midst of my second big CakePHP-based project for a client, still loving CakePHP and the MVC concept overall, but I am definitely having some headaches with CakePHP this time... »

Sponsored Links

  1. [...] to room34 for pointing this problem out… and, actually, he quite amusingly describes this and another important issue of CakePHP on his blog. I really suggest everyone gives it a read as is it will solve some mysteries of CakePHP [...]

  2. deizel says:

    Another suggestion could be to opt for a tinyint(2) field and store 0, 10, 20, 30, 40, 50, 60, 70, 80, 90 for your access_levels. This will help later if you decide you need more access levels as you can insert values between the others. Just a thought anyway. :)

  3. room34 says:

    Yes, I suppose I could do that. But is 100 really enough? ;)

    Right now I’ve only assigned 5 of the 10 available spaces anyway, and I’m still only really using three of those. For what it’s worth, here they are:

    0 = No access
    1 = Basic user (potentially a limited-access user, maybe read-only; I haven’t decided on a specific use for this yet)
    2 = Standard user
    8 = Administrator
    9 = Root (essentially the same as administrator, but I might want to have a “super administrator” at some point)

    So right now, it’s either 0, 2 or 8. Intuitive, right? :D

    My biggest fear about your suggestion is that it looks too much like BASIC to me. ’80s flashback.

  4. Richard@Home says:

    Sounds like this should be a foreign key to an roles table (or similar).

    Back on track , I hadn’t spotted this behaviour before – thanks for pointing it out – but I’d be very surprised if you couldn’t override this somewhere.

  5. room34 says:

    The roles table is not a bad idea; I’ve typically done that in the past. These days I’m focusing, however, on making my database schema as simple — minimalist, really — as possible. That means both keeping the number of tables low and keeping the number of fields in those tables low. Finding the common denominators between different types of content and building the database so that as much of the structure of the data itself can be handled from within the application as possible. That also means avoiding separate lookup tables when a simple numeric value or enum type field will do. I would have considered using an enum for this, actually, but I opted instead for the numeric values and defining constants for each to use within the application code itself. (A nice benefit of the numbers is that you can essentially grant access to “anyone at or above this level”… which of course could be done by reading the keys for the enum values too but… well, this approach seemed simpler and that’s what it’s all about.)

    As for the override… yes, I would think it is something you could override — both the handling of tinyint(1) fields and the automatic caching of database schema. But so far I haven’t found either… not that I’ve looked that hard.

  6. John LaSala says:

    Wow. If we’re hearing from even a fraction of 0.001% of you interested readership, you must have one heck of a sizable audience!

    ;-)

  7. I’m currently working on my first Cake project, but one of the first things that I did, for exactly the reasons you mention, is disable the cache in my core.php. This is in my dev environment, of course. That may help for the purpose of trying to get things done.

  8. room34 says:

    That’s a good idea. I think the reason I ran into this problem is something I can’t quite explain… this is my second “big” CakePHP project (although I’ve been poking around with it for nearly a year), building on some of the key elements of the first one. When I started the first one, version 1.2 wasn’t released yet, but I’ve upgraded the project to it. This second one was started on 1.2 right from the get-go.

    But… the model caching is just not happening on the first project. I didn’t even know it existed until I ran into this problem with the second project. I compared my core.php files between the two, and I don’t see any significant difference — the caching settings appear to be the same in both.

    Is model caching a new addition to version 1.2? When I upgraded the first project to 1.2, I replaced the cake directory, but I didn’t replace any of the files inside the app directory. Did I miss something critical in the upgrade?

  9. sourabh says:

    THANKS MAN, u have no idea how many hairs i have lost related to this fckuing tinyint bug. i tried everything except the “clear cache” thing to get it working. but nothing seemed to work.

    then i found ur post and whola…it actually works after clearing the cache….so THANKS AGAIN :D