I have this situation:
I have a table for Person (called persons). A person has a Name, an ID and a type (natural or juridical).
Then I have Organization, it of course is a juridical person that extends a Person and has specific fields like manager_id or employees.
Then I have an Authority which is a special government Organization, so in the end is also a Person and also has special required fields like decree_number (the number and year of the decree that constituted the Authority).
Then I have my database, where I have a table called persons.
My question is: should I create a new table for organizations and then another one for authorities? If not, how should I store the new fields that are required for Authorities or Organizations but not for a Persons?
I am doing this in a Laravel project, I could have:
/* Person.php */
class Person extends Model {
}
/* Organization.php */
class Organization extends Person {
protected $table = 'persons';
}
/* Authority.php */
class Authority extends Organization {
protected $table = 'persons';
}
Yes create another table but do not repeat the data which persons already has, you can just use relationships for that.
For instance, create a table called organizations which stores persons with organization level. The schema can be:
| person_id (FK to persons.id) | manager_id | other_fields |
Then another table for authorities which stores persons with authority level. The schema can be:
| person_id (FK to persons.id) | decree_number | other_fields |
Then in Laravel, just create a relationship, I'm assuming a one-to-one relationship here.
// Person.php
public function organizationProfile()
{
return $this->hasOne(Organization::class);
}
public function authorityProfile()
{
return $this->hasOne(Authority::class);
}
public function hasOrganization()
{
return $this->organizationProfile !== null;
}
public function hasAuthority()
{
return $this->authorityProfile !== null;
}
The design is like a Person can have profiles (which in your case is Organization and Authority). There could be other design but this is what I've thought of.
In summary, since Organization and Authority are both Persons with just another attribute, we just created another table to hold those attributes but not to repeat the attributes which are already present in Person. We will make use of relationships to determine if a Person is just a Person, an Organization or an Authority.
Related
I have sample workers table structure:
I have two type of workers: company workers and company branches workers. Type field can be only: 1 or 2. 1 - is company worker and 2 - is branch worker. How Now I can't write correct relationships method to get user company type (branch or company). Or I must create 2 tables (company_workers_table and company_branch_workers_table) for correctly write eloquent relationships?
You're looking for a polymorphic relationship:
https://laravel.com/docs/5.6/eloquent-relationships#polymorphic-relations
You want to set up your workers with two columns instead of just the type one, lets call this relation workplace. Your workers table would have workplace_id and workplace_type columns. The type columns holds the class of the related model (ie. App/Company) and your id column holds the ID of the related model, so the worker could be related to either a company or a company branch.
Your models will look something like this:
class Worker extends Model
{
public function workplace()
{
return $this->morphTo();
}
}
class Company extends Model
{
public function workers()
{
return $this->morphMany('App\Worker', 'workplace');
}
}
class Branch extends Model
{
public function workers()
{
return $this->morphMany('App\Worker', 'workplace');
}
}
I am about to implement a database for simple ecommerce platform. I want to implement the following:
Each product belongs to one product category;
Each product category has its own attributes;
Each product has one value for each attribute of this products type.
What relations should I use to store this kind of information?
Here is the logical model -- the way I understood it; you should be able to tweak it.
From this you can derive the physical model and the SQL code. The word KEY here means UNIQUE NOT NULL and you may use them for primary keys. Should you choose to introduce integers as primary keys, make sure you keep these UNIQUE.
Note that everything should be NOT NULL, once you get to the SQL.
Category named (CAT) exists.
Category {CAT}
KEY {CAT}
Attribute named (ATR) exists.
Attribute {ATR}
KEY {ATR}
Category (CAT) has attribute (ATR).
Each category has more than one attribute, it is possible for the same attribute to belong to more than one category.
CategoryAttribute {CAT, ATR}
KEY {CAT, ATR}
Product named (PRD) belongs to category (CAT).
Each product belongs to exactly one category, each category may have more than one product.
ProductCategory {PRD, CAT}
KEY {PRD}
KEY {PRD, CAT} -- seems redundant here, but is
-- needed for the FK from the next table
FOREIGN KEY {CAT} REFERENCES Category {CAT}
Product (PRD) from category (CAT) has attribute (ATR) that belongs to that category.
For each attribute that belongs to a category, that attribute may belong to more than one product from that category.
ProductCategoryAttribute {PRD, CAT, ATR}
KEY {PRD, CAT, ATR}
FOREIGN KEY {PRD, CAT} REFERENCES ProductCategory {PRD, CAT}
FOREIGN KEY {CAT, ATR} REFERENCES CategoryAttribute {CAT, ATR}
I don't know what database platform you are using, but for small numbers of products, and for queries that do not depend on the value of the per-category attributes, I'd use the following strategy:
CREATE TABLE "Category" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT
);
CREATE TABLE "Product" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
"categoryId" INTEGER NOT NULL REFERENCES "Category" ("id"),
"attributes" TEXT NOT NULL
);
In this example, the categories are used mainly to enforce referential integrity and to provide a list of categories for navigation.
The attributes are stored inside the attributes column as JSON (most modern databases tend to support this natively).
If there are any attributes common to all types of products, we'd create specific columns in Product. For example, you could add creationDate, deletionDate, price, or whatnot.
This allows you to perform the typical Select * From Product Where id = #Id to get a specific product and Select * From Product Where categoryId = #CategoryId to get all products in a category.
A creationDate could be useful to sort the products by creation date and take the top N, if necessary, when filtering by category. However with small quantities like thousands of products you might as well get all products by category and do this in code.
Regarding the code aspect, products like Dapper have specific extensions helping you deal with these discriminated unions, but writing code to support it is fairly easy. Here's an how. I'll write pseudo-C#, but I'm sure you can adapt.
We have an abstract class taking care of the Product table rows
public abstract class ProductBase
{
// only the fields in the Product table here
public int CategoryId { get; set; }
protected string Attributes { get; set; }
// serialize extra fields to JSON in Attributes
protected abstract void Prepare();
// load the common fields from a data row
protected static ProductBase(DataRow dr)
{
CategoryId = int.Parse(dr["categoryId"]);
Attributes = dr["attributes"] as string;
}
// save to DB
public void Save()
{
Prepare();
// save to SQL
}
}
We also have specific classes per category which have the extra attributes and handle serialization and deserialization.
public class FooProduct: ProductBase
{
public string Color { get; set; }
protected override void Prepare()
{
Attributes = Json.Serialize(new { Color });
}
public FooProduct(DataRow dr): base(dr)
{
// we can only create foo products if the category is foo
if (CategoryId != 23) throw new InvalidOperationException();
var attr = Json.Deserialize(Attributes);
Color = attr.Color;
}
}
This idea works great while you don't need to get the "foo" products by Color. If you can afford to get all "foo" products and filter in code, great. If your database understands JSON and lets you query inside the Attributes field, good it will get slow with large numbers unless the server allows indexes to reference JSON-serialized values.
If all else fails, you'll need to create an index table which contains the color values and the ids of the products which have that color. This is relatively painful and you don't want to do it unless you need it (and you don't right now).
I am new to grails and implementing one-to-one relationship in grails and trying to query on tables and not sure on how to represent one-to-one relationship in domain classes and query the results.
I have two tables
car(car_id number primary key,name varchar2(255))
engine(eng_id number primary key,name varchar2(255),car_id number (foreign key to car_id))
Domain Classes:
class Car {
..
static hasOne = [engine: Engine]
}
class Engine {
Car car
static belongsTo = [car : Car]
}
is the above one-to-ne relationship in domain class correct??
i need to query to get all the cars which have engines,should i use criteria query api or use HQL??
Any help appreciated.
class Engine {
//Car car
static belongsTo = [car : Car]
}
Just delete Car car and all be correct. For now u have 2 links to class Car in Engine
For a true one-to-one relationship use the hasOne property on the owning side e.g. Car:
class Car {
..
static hasOne = [engine: Engine]
}
class Engine {
Car car
static constraints = [
car unique: true
}
A good practice is to add a unique constrain on one side of the relationship. Click here to read the documentation.
What is the best way to model data for a job website which has the following elements:
Two types of user accounts: JobSeekers and Employers
Employers can create JobPost entities.
Each JobSeeker can create a Resume entity, and many JobApplication entities.
JobSeekers can create a JobApplication entity which is related to a JobPost entity.
A JobPost entity may receive many JobApplication entities.
A JobSeeker may only create one JobApplication entity per JobPost entity.
A Resume contains one or more instances of Education, Experience, using ndb.StructuredProperty(repeated = True).
Each Education contains the following ndb.StringProperty fields: institution, certification, area_of_study
While each Experience contains: workplace, job_title.
Here is a skeleton model that meets your requirements:
class Employer(ndb.Model):
user = ndb.UserProperty()
class JobPost(ndb.Model):
employer = ndb.KeyProperty(kind=Employer)
class JobSeeker(ndb.Model):
user = ndb.UserProperty()
def apply(self, job_post):
if JobApplication.query(JobApplication.job_seeker == self.key,
JobApplication.job_post == job_post).count(1) == 1:
raise Exception("Already applied for this position")
...
class Resume(ndb.Model):
job_seeker = ndb.KeyProperty(JobSeeker)
education = ndb.JsonProperty()
experience = ndb.JsonProperty()
class JobApplication(ndb.Model):
job_seeker = ndb.KeyProperty(JobSeeker)
job_post = ndb.KeyProperty(JobPost)
Notes:
Employer and JobSeeker have the built-in UserProperty to identify and allow them to login.
Resume uses JsonProperty for education and experience to allow for more fields in the future. You can assign a Python dictionary to this field, for example
resume.education = {'institution': 'name', 'certification': 'certificate', 'area_of_study': 'major', 'year_graduated': 2013, ...}
(I have personally found StructuredProperty to be more pain than gain, and I avoid it now.)
Limiting a JobSeeker to only one JobApplication can be done with the method apply() which checks the JobApplication table for existing applications.
The structure of concerning tables is as follows (MySQL):
//Table Name : team
tid PK
team_name (varchar)
//Table Name : fixture
fid PK
home_team_id FK |_ both referenced to 'tid' from 'team' table
away_team_id FK |
My aim is to retrieve the team names. Considering this structure, I think I'll have to retrieve home_team_id and away_team_id and then do something like
Fixture::where('tid','=',$home_team_id)->get();
My question is, is this the correct way to accomplish what I aim to do?
and
should this be done from the controller? (if so, then I'll have to do two queries from same function)
First, rather than having your primary keys be tid and fid, just keep them both as id. This is not only best practice, but will allow you to more easily use Laravel's Eloquent ORM as it by default assumes your primary key column is named id.
Second thing, make sure your table names are in plural form. Although this is not necessary, the example I'm about to give is using Laravel defaults, and Laravel assumes they are in plural form.
Anyway, once you've 'Laravelized' your database, you can use an Eloquent model to setup awesome relationships with very minimal work. Here's what I think you'd want to do.
app/models/Team.php
class Team extends Eloquent {
// Yes, this can be empty. It just needs to be declared.
}
app/models/Fixture.php
class Fixture extends Eloquent {
public function homeTeam()
{
return $this->belongsTo('Team', 'home_team_id');
}
public function awayTeam()
{
return $this->belongsTo('Team', 'away_team_id');
}
}
Above, we created a simple model Team which Laravel will automatically look for in the teams database table.
Second, we created model Fixture which again, Laravel will use the fixtures table for. In this model, we specified two relationships. The belongsTo relationship takes two parameters, what model it is related to, in both cases here they are teams, and what the column name is.
Laravel will automatically take the value in away_team_id and search it against the id column in your teams table.
With just this minimal amount of code, you can then do things like this.
$fixture = Fixture::find(1); // Retrieves the fixture with and id of 1.
$awayTeam = $fixture->awayTeam()->first(); // var_dump this to see what you get.
$homeTeam = $fixutre->homeTeam()->first();
Then you can proceed as normal and access the column names for the tables. So say you have a 'name' column in the teams table. You can echo out the the home team name from the fixture like so.
$fixture = Fixture::find(1); // Get the fixture.
echo $fixture->homeTeam->name;
It's nearly 2AM, so there might be an error or two above, but it should work.
Make sure you check the docs for Eloquent, especially the bits relating to relationships. Remember to name your columns and tables in the way Laravel wants you to. If you don't, there are ways to specify your custom names.
If you want to get even more fancy, you can define the inverse relationship like this on your Team model.
app/models/Team.php
class Team extends Eloquent {
public function fixturesAtHome()
{
return $this->hasMany('Fixture', 'home_team_id');
}
public function fixturesAway()
{
return $this->hasMany('Fixture', 'away_team_id');
}
}
Then to get all of a particular team's home fixtures...
$team = Team::find(1); // Retreive team with id of 1;
$homeFixtures = $team->fixturesAtHome();