Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F298821
FinsModel.pike
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Dec 25 2024, 8:29 PM
Size
12 KB
Mime Type
text/plain
Expires
Fri, Dec 27, 8:29 PM (4 w, 1 d ago)
Engine
blob
Format
Raw Data
Handle
60939
Attached To
R148 fins
FinsModel.pike
View Options
import
Fins
;
inherit
FinsBase
;
int
lower_case_link_names
=
0
;
object
log
=
Tools
.
Logging
.
get_logger
(
"model"
);
static
void
create
(
Fins
.
Application
a
)
{
::
create
(
a
);
load_model
();
}
//! configures any models defined in the application's configuration file. this method is automatically called
//! by the constructor.
//!
//! an application normally contains a default model, which is specified in the "model" section of the application's
//! configuration file. additional model definitions can be specified in the configuration file by adding sections
//! whose name is in the form of "model_x" where x is some unique identifier.
//!
//! valid configuration values for a model definition include "id", "datasource", "definition_module" and "debug".
//! The "datasource" attribute is mandatory for all model definitions; "definition_module" and "id" are mandatory for
//! all definitions other than the default, and all other attributes are optional for all model definitions.
//!
//! when each model is loaded, a database connection will be made and all registered data types will be configured,
//! either manually or automatically using database reflection. Additionally the default context for each model
//! definition will be registered in @[Fins.DataSources] using the value of its "id" attribute as the key.
//!
//! in addition to being available from Fins.Model, the default model is also available from @[Fins.Datasources] by its
//! "id" attribute, or if no "id" is specified, using the key "_default".
//!
//! @example
//! // get a context for the default model
//! Fins.Model.DataModelContext ctx = Fins.DataSources._default;
//! // get a context for the model whose configuration specifies an "id" of "my_additional_model"
//! Fins.Model.DataModelContext ctx2 = Fins.DataSources.my_additional_model;
//!
void
load_model
()
{
object
context
=
configure_context
(
config
[
"model"
],
1
);
Fins
.
Model
.
set_context
(
"_default"
,
context
);
if
(
config
[
"model"
][
"id"
])
Fins
.
Model
.
set_context
(
config
[
"model"
][
"id"
],
context
);
foreach
(
glob
(
"model_*"
,
config
->
get_sections
());;
string
md
)
{
log
->
info
(
"configuring model id <"
+
config
[
md
][
"id"
]
+
"> specified in config secion "
+
md
);
object
ctx
=
configure_context
(
config
[
md
],
0
);
Fins
.
Model
.
set_context
(
config
[
md
][
"id"
],
ctx
);
}
}
object
get_context
(
mapping
config_section
)
{
return
master
()
->
resolv
(
"Fins.Model.DataModelContext"
)();
}
object
get_repository
(
mapping
config_section
)
{
return
Fins
.
Model
.
Repository
();
}
object
configure_context
(
mapping
config_section
,
int
is_default
)
{
object
repository
;
string
definition_module
;
repository
=
get_repository
(
config_section
);
object
o
;
object
defaults
=
Fins
.
Helpers
.
Defaults
;
catch
(
defaults
=
(
object
)
"defaults"
);
if
(
is_default
)
definition_module
=
(
config_section
->
definition_module
||
config
->
app_name
);
else
definition_module
=
config_section
->
definition_module
;
if
(
!
definition_module
)
{
throw
(
Error
.
Generic
(
"No model definition module specified. Cannot configure model."
));
}
if
(
o
=
master
()
->
resolv
(
definition_module
+
"."
+
defaults
->
data_mapping_module_name
))
repository
->
set_model_module
(
o
);
if
(
o
=
master
()
->
resolv
(
definition_module
+
"."
+
defaults
->
data_instance_module_name
))
repository
->
set_object_module
(
o
);
string
url
=
config_section
[
"datasource"
];
object
d
=
get_context
(
config_section
);
d
->
context_id
=
config_section
[
"id"
]
||
"_default"
;
d
->
set_url
(
url
);
d
->
sql
=
d
->
get_connection
();
d
->
debug
=
(
int
)
config_section
[
"debug"
];
d
->
repository
=
repository
;
d
->
cache
=
cache
;
d
->
app
=
app
;
d
->
model
=
this
;
d
->
initialize
();
repository
->
set_default_context
(
d
);
register_types
(
d
);
initialize_links
(
d
);
rebuild_fields
(
d
);
return
d
;
}
void
rebuild_fields
(
object
ctx
)
{
foreach
(
ctx
->
repository
->
object_definitions
;;
object
d
)
{
d
->
gen_fields
(
ctx
);
}
}
//!
void
register_types
(
object
ctx
)
{
if
(
!
ctx
->
repository
->
get_object_module
())
{
log
->
warn
(
"Using automatic model registration, but no datatype_definition_module set. Skipping."
);
return
0
;
}
object
im
=
ctx
->
repository
->
get_object_module
();
object
mm
=
ctx
->
repository
->
get_model_module
();
werror
(
"mm: %O
\n
"
,
mm
);
foreach
(
mkmapping
(
indices
(
mm
),
values
(
mm
));
string
n
;
program
c
)
{
object
d
=
c
(
ctx
);
program
di
;
if
(
im
&&
im
[
n
])
{
di
=
im
[
n
];
if
(
di
&&
!
di
->
type_name
)
{
/*werror("%O\n", di);di->type_name = n;*/
}
}
else
{
throw
(
Fins
.
Errors
.
ModelError
(
"No Data Instance class defined for data type "
+
n
+
" in model id "
+
ctx
->
context_id
+
"."
));
}
log
->
info
(
"Registering data type %s"
,
d
->
instance_name
);
ctx
->
repository
->
add_object_type
(
d
,
di
);
}
}
protected
void
remove_field_from_possibles
(
object
ctx
,
string
field_name
,
string
instance_name
)
{
foreach
(
ctx
->
builder
->
possible_links
;
int
i
;
mapping
pl
)
{
if
(
pl
&&
pl
->
obj
&&
pl
->
obj
->
instance_name
==
instance_name
&&
pl
->
field
->
name
==
field_name
)
ctx
->
builder
->
possible_links
[
i
]
=
0
;
}
}
// there be monsters here...
void
initialize_links
(
object
ctx
)
{
if
(
!
ctx
->
repository
->
object_definitions
||
!
sizeof
(
ctx
->
repository
->
object_definitions
))
return
0
;
foreach
(
ctx
->
builder
->
belongs_to
;;
mapping
a
)
{
log
->
debug
(
"processing belongs_to: %O"
,
a
);
if
(
!
ctx
->
repository
->
get_object
(
a
->
other_type
))
{
log
->
error
(
"error processing %O->belongs_to because the type %O does not exist."
,
a
->
obj
,
a
->
other_type
);
}
if
(
!
a
->
my_name
)
a
->
my_name
=
a
->
other_type
;
if
(
!
a
->
my_field
)
a
->
my_field
=
lower_case
(
a
->
other_type
+
"_"
+
ctx
->
repository
->
get_object
(
a
->
other_type
)
->
primary_key
->
field_name
);
string
my_name
=
a
->
my_name
;
string
my_field
=
a
->
my_field
;
if
(
lower_case_link_names
)
{
my_name
=
lower_case
(
my_name
);
}
a
->
obj
->
add_field
(
ctx
,
Model
.
KeyReference
(
my_name
,
my_field
,
a
->
other_type
,
0
,
a
->
nullable
));
remove_field_from_possibles
(
ctx
,
my_field
,
a
->
other_type
);
}
foreach
(
ctx
->
builder
->
has_many
;;
mapping
a
)
{
if
(
!
a
->
my_name
)
a
->
my_name
=
Tools
.
Language
.
Inflect
.
pluralize
(
a
->
other_type
);
if
(
!
a
->
other_field
)
a
->
other_field
=
ctx
->
repository
->
get_object
(
a
->
other_type
)
->
primary_key
->
name
;
string
my_name
=
a
->
my_name
;
string
other_field
=
a
->
other_field
;
if
(
lower_case_link_names
)
{
my_name
=
lower_case
(
my_name
);
}
a
->
obj
->
add_field
(
ctx
,
Model
.
InverseForeignKeyReference
(
my_name
,
/*Tools.Language.Inflect.singularize*/
(
a
->
other_type
),
other_field
));
remove_field_from_possibles
(
ctx
,
other_field
,
a
->
other_type
);
}
foreach
(
ctx
->
builder
->
has_many_index
;;
mapping
a
)
{
if
(
!
a
->
my_name
)
a
->
my_name
=
Tools
.
Language
.
Inflect
.
pluralize
(
a
->
other_type
);
// werror("we'll call the field " + a->my_name + "\n");
if
(
!
a
->
other_field
)
a
->
other_field
=
ctx
->
repository
->
get_object
(
a
->
other_type
)
->
primary_key
->
name
;
string
my_name
=
a
->
my_name
;
string
other_field
=
a
->
other_field
;
string
index_field
=
a
->
index_field
;
if
(
lower_case_link_names
)
{
my_name
=
lower_case
(
my_name
);
}
a
->
obj
->
add_field
(
ctx
,
Model
.
MappedForeignKeyReference
(
my_name
,
/*Tools.Language.Inflect.singularize*/
(
a
->
other_type
),
other_field
,
index_field
));
remove_field_from_possibles
(
ctx
,
other_field
,
a
->
other_type
);
}
foreach
(
ctx
->
builder
->
has_many_many
;;
mapping
a
)
{
object
this_type
;
object
that_type
;
this_type
=
ctx
->
repository
->
object_definitions
[
a
->
this_type
->
instance_name
];
that_type
=
ctx
->
repository
->
object_definitions
[
a
->
that_type
];
log
->
debug
(
"*** have a Many-to-Many relationship in %s between %O and %O"
,
a
->
join_table
,
this_type
,
that_type
);
string
this_name
=
a
->
this_name
;
string
that_name
=
a
->
that_name
;
if
(
lower_case_link_names
)
{
this_name
=
lower_case
(
this_name
);
that_name
=
lower_case
(
that_name
);
}
this_type
->
add_field
(
ctx
,
Model
.
MultiKeyReference
(
this_type
,
Tools
.
Language
.
Inflect
.
pluralize
(
that_name
),
a
->
join_table
,
lower_case
(
this_type
->
instance_name
+
"_"
+
this_type
->
primary_key
->
field_name
),
lower_case
(
that_type
->
instance_name
+
"_"
+
that_type
->
primary_key
->
field_name
),
that_type
->
instance_name
,
that_type
->
primary_key
->
name
));
that_type
->
add_field
(
ctx
,
Model
.
MultiKeyReference
(
that_type
,
Tools
.
Language
.
Inflect
.
pluralize
(
this_name
),
a
->
join_table
,
lower_case
(
that_type
->
instance_name
+
"_"
+
that_type
->
primary_key
->
field_name
),
lower_case
(
this_type
->
instance_name
+
"_"
+
this_type
->
primary_key
->
field_name
),
this_type
->
instance_name
,
this_type
->
primary_key
->
name
));
}
foreach
(
ctx
->
builder
->
possible_links
;;
mapping
pl
)
{
if
(
!
pl
)
continue
;
log
->
debug
(
"investigating possible link field %s in %s."
,
pl
->
field
->
name
,
pl
->
obj
->
instance_name
);
string
pln
=
lower_case
(
pl
->
field
->
name
);
foreach
(
ctx
->
repository
->
object_definitions
;
string
on
;
object
od
)
{
string
mln
=
Tools
.
Language
.
Inflect
.
singularize
(
od
->
table_name
)
+
"_"
+
od
->
primary_key
->
field_name
;
log
->
debug
(
" - considering %s as a possible field linkage."
,
mln
);
if
(
pln
==
lower_case
(
mln
))
{
log
->
debug
(
"adding reference for %s in %s id=%O"
,
od
->
instance_name
,
pl
->
obj
->
instance_name
,
pl
->
obj
->
primary_key
->
name
);
string
this_name
=
od
->
instance_name
;
string
that_name
=
pl
->
obj
->
instance_name
;
if
(
lower_case_link_names
)
{
this_name
=
lower_case
(
this_name
);
that_name
=
lower_case
(
that_name
);
}
pl
->
obj
->
add_field
(
ctx
,
Model
.
KeyReference
(
this_name
,
pl
->
field
->
name
,
od
->
instance_name
,
0
,
!
(
pl
->
field
->
not_null
)));
od
->
add_field
(
ctx
,
Model
.
InverseForeignKeyReference
(
Tools
.
Language
.
Inflect
.
pluralize
(
that_name
),
pl
->
obj
->
instance_name
,
this_name
));
ctx
->
builder
->
possible_links
-=
({
pl
});
}
}
}
array
table_components
=
({});
foreach
(
ctx
->
repository
->
object_definitions
;
string
on
;
object
od
)
{
table_components
+=
({
([
"tn"
:
lower_case
(
Tools
.
Language
.
Inflect
.
pluralize
(
on
)),
"od"
:
od
])
});
}
// Now, we check for link-tables, that is, tables which represent a many-to-many relationship
// between two datatypes using a table that contains tuples of the two id fields.
//
// We do this automatically by searching for any combination of tablea_tableb in the database which
// conforms to the practice of having the fields representing the two linked tables called typeA_id
// and typeB_id where typeA and typeB are singular forms of the table name.
//
// for example a table used to link user and group would be called users_groups and have fields called
// user_id and group_id.
multiset
available_tables
=
(
multiset
)
ctx
->
sql
->
list_tables
();
foreach
(
table_components
;;
mapping
o
)
{
log
->
debug
(
"looking for multi link reference for %s."
,
o
->
tn
);
foreach
(
table_components
;;
mapping
q
)
{
if
(
q
->
tn
==
o
->
tn
)
continue
;
// skip self-self relationships :)
log
->
debug
(
" - checking %s->%s."
,
q
->
tn
,
o
->
tn
+
"_"
+
q
->
tn
);
if
(
available_tables
[
o
->
tn
+
"_"
+
q
->
tn
])
{
log
->
debug
(
" - have a mlr on %s"
,
o
->
tn
+
"_"
+
q
->
tn
);
string
this_name
=
q
->
od
->
instance_name
;
string
that_name
=
o
->
od
->
instance_name
;
if
(
lower_case_link_names
)
{
this_name
=
lower_case
(
this_name
);
that_name
=
lower_case
(
that_name
);
}
o
->
od
->
add_field
(
ctx
,
Model
.
MultiKeyReference
(
o
->
od
,
Tools
.
Language
.
Inflect
.
pluralize
(
this_name
),
o
->
tn
+
"_"
+
q
->
tn
,
lower_case
(
o
->
od
->
instance_name
+
"_"
+
o
->
od
->
primary_key
->
field_name
),
lower_case
(
q
->
od
->
instance_name
+
"_"
+
q
->
od
->
primary_key
->
field_name
),
q
->
od
->
instance_name
,
q
->
od
->
primary_key
->
name
));
q
->
od
->
add_field
(
ctx
,
Model
.
MultiKeyReference
(
q
->
od
,
Tools
.
Language
.
Inflect
.
pluralize
(
that_name
),
o
->
tn
+
"_"
+
q
->
tn
,
lower_case
(
q
->
od
->
instance_name
+
"_"
+
q
->
od
->
primary_key
->
field_name
),
lower_case
(
o
->
od
->
instance_name
+
"_"
+
o
->
od
->
primary_key
->
field_name
),
o
->
od
->
instance_name
,
o
->
od
->
primary_key
->
name
));
}
}
}
log
->
debug
(
"possible links left over: %O"
,
ctx
->
builder
->
possible_links
);
foreach
(
ctx
->
builder
->
possible_links
;;
mapping
pl
)
{
if
(
pl
)
pl
->
obj
->
do_add_field
(
ctx
,
pl
->
field
);
}
ctx
->
builder
->
belongs_to
=
({});
ctx
->
builder
->
has_many
=
({});
ctx
->
builder
->
has_many_many
=
({});
}
Event Timeline
Log In to Comment