LJSUP-17669: Login.bml form refactoring
[livejournal.git] / cgi-bin / LJ / Typemap.pm
blob6c3d30eb800e21f309b184ac89e3de3515b09d48
1 # This class is used to keep track of a typeid => class name
2 # mapping in the database. You create a new typemap object and
3 # describe the table to it (tablename, class field name, id field name)
4 # You can then look up class->typeid or vice-versa on the Typemap object
5 # Mischa Spiegelmock, 4/21/06
7 use strict;
8 package LJ::Typemap;
9 use Carp qw/croak/;
11 *new = \&instance;
13 my %singletons = ();
15 # class method
16 # table is the typemap table
17 # the fields are the names of the fields in the table
18 sub instance {
19 my ($class, %opts) = @_;
21 my $table = delete $opts{table} or croak "No table";
22 my $classfield = delete $opts{classfield} or croak "No class field";
23 my $idfield = delete $opts{idfield} or croak "No id field";
25 croak "Extra args passed to LJ::Typemap->new" if %opts;
27 return $singletons{$table} if $singletons{$table};
29 croak "Invalid arguments passed to LJ::Typemap->new"
30 unless ($class && $table !~ /\W/g && $idfield !~/\W/g && $classfield !~/\W/g);
32 my $self = {
33 table => $table,
34 idfield => $idfield,
35 classfield => $classfield,
36 loaded => 0,
37 cache => {},
40 return $singletons{$table} = bless $self, $class;
43 # just what it says
44 # errors hard if you look up a typeid that isn't mapped
45 sub typeid_to_class {
46 my ($self, $typeid) = @_;
48 $self->_load unless $self->{loaded};
49 my $proc_cache = $self->{cache};
51 my ($class) = grep { $proc_cache->{$_} == $typeid } keys %$proc_cache;
53 croak "No class for id $typeid on table $self->{table}" unless $class;
55 return $class;
58 # this will return the typeid of a given class.
59 # if there is no typeid for this class, it will create one.
60 # returns undef on failure
61 sub class_to_typeid {
62 my ($self, $class) = @_;
64 croak "No class specified in class_to_typeid" unless $class;
66 $self->_load unless $self->{loaded};
67 my $proc_cache = $self->{cache};
68 my $classid = $proc_cache->{$class};
69 return $classid if defined $classid;
71 # this class does not have a typeid. create one.
72 my $dbh = LJ::get_db_writer();
74 my $table = $self->{table} or croak "No table";
75 my $classfield = $self->{classfield} or croak "No class field";
76 my $idfield = $self->{idfield} or croak "No id field";
78 # try to insert
79 $dbh->do("INSERT INTO $table ($classfield) VALUES (?)",
80 undef, $class);
82 unless ($dbh->err) {
83 # inserted fine, get ID
84 $classid = $dbh->{'mysql_insertid'};
85 } else {
86 # race condition, try to select again
87 $classid = $dbh->selectrow_array("SELECT $idfield FROM $table WHERE $classfield = ?",
88 undef, $class)
89 or die "Typemap could not generate ID after race condition";
92 # we had better have a classid by now... big trouble if we don't
93 die "Could not create typeid for table $table class $class" unless $classid;
95 # save new classid
96 $proc_cache->{$class} = $classid;
98 $self->proc_cache_to_memcache;
100 return $classid;
103 # given a list of classes, create an ID for each if no ID exists
104 # returns list of corresponding IDs
105 sub map_classes {
106 my ($self, @classes) = @_;
108 $self->_load or die;
110 my @ids;
112 foreach my $class (@classes) {
113 # just ask for the typeid of this class
114 push @ids, $self->class_to_typeid($class);
117 return @ids;
120 # delete a class->id map
121 # returns not undef on success
122 sub delete_class {
123 my ($self, $class) = @_;
125 my $dbh = LJ::get_db_writer() or die "No DB writer";
127 my $table = $self->{table} or die "No table";
128 my $classfield = $self->{classfield} or return undef;
130 $dbh->do("DELETE FROM $table WHERE $classfield=?", undef, $class) or return undef;
132 delete $self->{cache}->{$class};
133 $self->proc_cache_to_memcache;
135 return 1;
138 # save the process cache to memcache
139 sub proc_cache_to_memcache {
140 my $self = shift;
141 my $table = $self->{table};
142 # memcache typeids
143 LJ::MemCache::set("typemap_$table", $self->{cache}, 120);
146 # returns an array of all of the classes in the table
147 sub all_classes {
148 my $self = shift;
150 $self->_load or die;
151 return keys %{$self->{cache}};
154 # makes sure typemap cache is loaded
155 sub _load {
156 my $self = shift;
158 $self->{loaded} = 1;
160 my $table = $self->{table} or die "No table";
161 my $classfield = $self->{classfield} or die "No class field";
162 my $idfield = $self->{idfield} or die "No id field";
164 my $proc_cache = $self->{cache};
166 # is it memcached?
167 my $memcached_typemap = LJ::MemCache::get("typemap_$table");
168 if ($memcached_typemap) {
169 # process-cache it
170 $proc_cache = $memcached_typemap;
173 my $dbr = LJ::get_db_reader();
174 return undef unless $dbr;
176 # load typemap from DB
177 my $sth = $dbr->prepare("SELECT $classfield, $idfield FROM $table");
178 die $dbr->errstr if $dbr->errstr;
179 return undef unless $sth;
181 $sth->execute;
182 die $dbr->errstr if $dbr->errstr;
184 while (my $idmap = $sth->fetchrow_hashref) {
185 $proc_cache->{$idmap->{$classfield}} = $idmap->{$idfield};
188 # save in memcache
189 $self->proc_cache_to_memcache;
191 $self->{cache} = $proc_cache;
193 return $proc_cache;