DBIx::Class virtual column? - perl

I am building an app with a DBIx::Class (Loader) based ORM. Most of my database models have a 'name' column. One of my controllers searches all schema classes using primarily the 'name' column. A couple of schema classes however don't have a 'name' column.
Is it possible in DBIx::Class to add a sort of 'virtual' column that uses another column instead:
$resultset('Account')->search({name => 'foobar'})
secretly rewrites to
$resultset('Account')->search({accountnumber => 'foobar'})
I hope I am making sense, anyone?
Thx,
Rob

As far as I know this is not possible - not as a key in a search query anyhow. What you could do is create a base-class for all your resultset classes (you are using load_namespaces, right?) which has a method find_by_name or similar, that performs this search on the correct column. The column could default to name but be overridden by a class attribute - which you can set up with mk_accessor.
You can set this base-class to be the default resultset class for all your resultsets with the default_resultset_class attribute of load_namespaces

Related

Rails 5.1.2 - Single Table Inheritance: No migration is getting generated

I am trying to generate scaffolding for STI implementation. I issue the following.
rails g scaffold user1 type name email
rails g scaffold member company subscription --parent user1
Every thing gets generated file except for the migration file my 'member' model.
When I try to create a member record like this
Member.create(name: "My Name", email: "myname#example.com",
company: 'Example LLC', subscription: 'Monthly Gold' )
I get this error:
ActiveModel::UnknownAttributeError: unknown attribute 'company' for Member. from (irb):1
Any ideas on what is going on?
I use rails 5 and db is postgres
The --parent option assumes that you are already all setup for single table inheritance, i.e. the parent class has a table with a type column (or whatever column you are using for this).
Since the model will be stored in the parent's table, there is no need to create a new table for the subclass, hence no migration
I got this answer similar to this question asked by someone.
To my understanding, you are on the wrong track. In single table inheritance, all the attributes must be present in the parent model table with an additional column name 'type' to indicate the type of inherited model. The column name 'type' can be changed with appropriate settings but ActiveRecord by default looks for 'type' column. You are getting 'UnknownAttributeError' error cause the parent model does not have the following column in its table. You need to write a migration to add the new columns. Hope you understand the concept of STI. For further exploration, I am providing you the link of the official guide. Hope your problem will be solved.
http://edgeguides.rubyonrails.org/association_basics.html#single-table-inheritance

Eloquent Friendly Column Name

We're currently transitioning from one database to another. A table in our legacy database has column names that are less than ideal, for example:
some_crazy_name__xyz
In our new database, we'd like to have a column name like:
someCrazyName
In the short term, we have to work with data from our legacy database. At some point in the near future, we'd like to switch over without having to refactor all of our Eloquent code to query for different column names. For example:
$model = MyModel::where('someCrazyName', '=', 1);
I'm currently extending the Model class, where all implementing models provide a map of terrible names to friendly names:
class MyModel extends BaseModel {
$columnMap = array(
'someCrazyName' => 'some_crazy_name__xyz'
);
}
This works well where I can use __get and __set in BaseModel to lookup properties in my map, for example:
$myModel = new MyModel;
// ...
echo $myModel->someCrazyName;
However, this obviously doesn't work well with queries without having to always use my map to look up column names. I'm wondering if it's possible without having to override all of the methods within Illuminate\Database\Eloquent\Model, Illuminate\Database\Query\Builder and Illuminate\Database\Eloquent\Builder that deal with columns, where the underlying query that is built always maps to the correct column? Then after we transition databases, we can remove that one piece of code rather then remove potentially thousands of column name mappings.
This is what you need: https://github.com/jarektkaczyk/eloquence/wiki/Mappable
It's not only for mapping badly_named_columns to something_useful, but also can be used for relational mappings:
// simple aliasing
User::where('cool_name', 'value') // where badName = ?
// relations, eg. User hasOne Profile
User::where('first_name', 'Jon') // search through related profiles table
// and obviously mutators:
$user->first_name == $user->profile->first_name
$user->cool_name = 'Jon' // becomes $user->badName = 'value'
$user->cool_name; // 'Jon'
One way to do it would be with accessors.
For example, in MyModel you could define an accessor for the some_crazy_name__xyz column like this:
public function getSomeCrazyNameAttribute()
{
return $this->attributes['some_crazy_name__xyz'];
}
You can then transparently refer to that column with $mymodel->someCrazyName. You can also define a mutator to set the value.
Admittedly, this may not be the best solution if you have MANY values like this. But it does have one important benefit: later on, if you refactor your database so that the column some_crazy_name__xyz is actually called someCrazyName, all you need to do is remove that function from your model. And, to my mind at least, it's simpler than trying to override a bunch of methods on the various classes involved.
And unfortunately, it doesn't adequately address the use of column names in queries. For that, you might want to look at the repository pattern. But in any event, it looks like there's going to be a lot of coding involved.
Finally, you haven't mentioned what database you're using. If it's MySQL, it is possible to create updatable and insertable views. Using a view, you could simply map old column names to new, and point your Eloquent model at the view instead of a table. Other database servers may provide similar functionality.

How to Use inheritance in EF

I am using EF 4.0 , i have one problem
Table structure in DB is:
Table: Setting--->
Name (PK)
GroupBy
DataType
Table: UserSetting-->
SettingName(PK)(FK)
UserName(PK)(FK)
Value
Table: WorkstationSetting-->
SettingName(PK)(FK)
WorkstationName(PK)(FK)
Value
Now i want to make use of inheritance, because WorkstationSetting and UserSetting inherits settings so any suggestion how to achieve inheritance, i tried but i got error like
"Error 39 Error 3003: Problem in mapping fragments starting at line 1621:All the key properties (Settings.Name) of the EntitySet Settings must be mapped to all the key properties (WorkstationSetting.SettingName, WorkstationSetting.WorkstationName) of table WorkstationSetting.
I see you have in UserSetting and WorkstationSetting a composite PK.
If UserSetting and WorkstationSetting are derived from Setting, they should have Name as PK.
Another comment; in general, it's not recommended to use a name or something "meaningful" as PK since it is less scalable and might cause limitations (i.e. max index size). Use instead an int or uniqueidentifier.
I recommend you to introduce a new field which is SettingId which should be added to all three tables. In EF designer, just add the Inheritance.
Look into table per type inheritance. For example look here. It should help you get started. The idea is that you have a table for each concrete type (as you have) and you map it to an object hierarchy.
Maybe your problem is with the keys. How is your mapping defined? Are the associations between the tables defined in the DB?

Access the property used in mapping entity to a table in EFv4

When we have two entities in EFv4 EDM diagram and only one table for both in the database (for instance, having table Documents and entities Invoice and Qoute), table Documents having documentTypeId column as a discriminator and set this column as a discriminator in the EDM (in Table mappings), how do we read the value of this property in our code?
We cannot assign values to it because EF does it for us under the hood (based on what we entered in Table mappings for condition) but somehow I don't get it why we are also not allowed to read it.
Imo this property is already mapped so you can't map it again. It is used to determine type of materialized entity. Why do you need such column. Usually it is enough to use is operator like:
var document = context.Documents.GetById(id);
if (document is Invoice)
{
...
}
If you only need to select subtypes you can use OfType extension method like:
var invoices = context.Documents.OfType<Invoice>().ToList();
You also don't need to set this value when adding new entity because you are adding subtype - Invoice or Quote.
Edit:
As I understand from your comment you don't need this information in query. In such case you don't need to map it. Simply use partial class of your entity and add custom property which will return your string. Sound like stupid solution but actually it would be the easiest one.
Discriminator column should be part of mapping metadata so in case of T4 template generating your entities, it could be possible to update the template so it generate such property for you.
You may want to use a single-table inheritance hierarchy, as described here.
That way, you could have an abstract Document class that includes a DocumentTypeId column. Invoices and Quotes would extend this class, but specify certain DocumentTypeId filters. However, because the original class has a DocumentTypeId column, they would each have that column as well.
Another advantage to this approach is that you could create utility methods that can act on any Document, and you could pass any Invoice or Quote to these methods.

EF Table-per-hierarchy mapping

In trying to normalize a database schema and mapping it in Entity Framework, I've found that there might end up being a bunch of lookup tables. They would end up only containing key and value pairs. I'd like to consolidate them into one table that basically has two columns "Key" and "Value". For example, I'd like to be able to get Addresses.AddressType and Person.Gender to both point to the same table, but ensure that the navigation properties only return the rows applicable to the appropriate entity.
EDIT: Oops. I just realized that I left this paragraph out:
It seems like a TPH type of problem, but all of the reading I've done indicates that you start with fields in the parent entity and migrate fields over to the inherited children. I don't have any fields to move here because there would generally only be two.
There are a lot of domain-specific key-value pairs need to be represented. Some of them will change from time to time, others will not. Rather than pick and choose I want to just make everything editable. Due to the number of these kinds of properties that are going to be used, I'd rather not have to maintain a list enums that require a recompile, or end up with lots of lookup tables. So, I thought that this might be a solution.
Is there a way to represent this kind of structure in EF4? Or, am I barking up the wrong tree?
EDIT: I guess another option would be to build the table structure I want at the database level and then write views on top of that and surface those as EF entities. It just means any maintenance needs to be done at multiple levels. Does that sound more, or less desireable than a pure EF solution?
Table per hiearchy demands that you have one parent entity which is used as base class for child entities. All entities are mapped to the same table and there is special discriminator column to differ type of entity stored in database record. You can generally use it even if your child entities do not define any new properties. You will also have to define primary key for your table otherwise it will be handled as readonly entity in EF. So your table can look like:
CREATE TABLE KeyValuePairs
(
Id INT NOT NULL IDENTITY(1,1),
Key VARCHAR(50) NOT NULL,
Value NVARCHAR(255) NOT NULL,
Discriminator VARCHAR(10) NOT NULL,
Timestamp Timestamp NOT NULL
)
You will define your top level KeyValuePair entity with properties Id, Key, Value and Timestamp (set as concurrency mode fixed). Discriminator column will be used for inheritance mapping.
Be aware that EF mapping is static. If you define AddressType and Gender entities you will be able to use them but you will not be able to dynamically define new type like PhoneType. This will always require modifying your EF model, recompiling and redeploying your application.
From OOP perspective it would be nicer to not model this as object hiearchy and instead use conditional mapping of multiple unrelated entities to the same table. Unfortunatelly even EF supports conditional mapping I have never been able to map two entities to the same table yet.