Actually I'm confused for the case, which relation fits best for my case, and in my opinion the best one is to have a table with 3 primary keys.
To be more specific.
I have a Person model in one of my db's, which has structure like
Person:
Id,
FirstName,
LastName,
...
And the other model Department, which has structure mentioned below
Department:
Id,
Name,
Description,
...
And goal is to set up Editors of schedule for each department and add also admins, whioch will approve requested schedules from editors. Editors and Admins are from same Person table, and if to assume, we need to map some Persons and department with some type.
I'm thinking about to have a mapping table with structure
PersonID,
DepartmentID,
Type (editor or admin)
And not sure, which relation fits best for this. If to have belongsToMany relation here with primary keys PersonID and DepartmentID, we will face an issue, because same Person possibly can be as editor and as admin for one single department. I have MS SQL server as a db.
Any suggestions will be appreciated
you can define many to many relations and use wherePivot method to select by pivot table Type column:
// Department model
public function admins()
{
return $this->belongsToMany(Person::class)->wherePivot('type', 'admin');
}
public function editors()
{
return $this->belongsToMany(Person::class)->wherePivot('type', 'editor');
}
// Person model
public function departmentsWhereIsAdmin()
{
return $this->belongsToMany(Department::class)->wherePivot('type', 'admin');
}
public function departmentsWhereIsEditor()
{
return $this->belongsToMany(Department::class)->wherePivot('type', 'editor');
}
// Note: we use methods names without parentheses
// usage for department
$department = Department::first(); // for example
dump($department->admins);
dump($department->editors);
// usage for person
$person = Person::first(); // for example
dump($person->departmentsWhereIsAdmin);
dump($person->departmentsWhereIsEditor);
Related
I am using Golang (GORM) + Postgres. I am trying to model a business situation where a seller sells things to buyers, each creating an order transaction.
I have an Order Gorm model, as well as a Buyer and a Seller Gorm model. The Buyer and the Seller are already rows created in the database.
One buyer HasMany orders.
One seller HasMany orders.
To map out this relation, I believe I just create the respective Buyer/Seller struct (standard Gorm models), and then make an Order struct like so:
type Order struct {
ID int64 `json:"ID"gorm:"primary_key;auto_increment:true"`
Buyer *Buyer `json:"Buyer"`
Seller *Seller `json:"Seller"`
// ... other data ...
}
I'm assuming the above automatically creates the relationship, but I am not entirely sure. I can create an Order with this function, and this returns fine:
func CreateOrder(buyer *entity.Buyer, seller *entity.Seller) (*entity.Order, error) {
order := &entity.Order{
User: buyer,
Sitter: seller,
// ... other data ...
}
db.Table("orders").Create(order)
return order
}
If I go to Postgres CLI, the TABLE orders; does not show the columns buyer or seller. I would expect a column of their IDs. So this is why I am unsure this is working. This could definitely be a problem in itself.
Anyways, what I really want to do is be able to check if any orders currently exist for a Buyer / Seller. But I don't really see anyway to do that with gorm queries. I would imagine in SQL it would be something like:
func FindOrder(buyer *entity.Buyer, seller *entity.Seller) {
db.Raw(`GET order FROM orders WHERE buyer = ?, seller = ?`, buyer, seller)
// OR this ???
db.Table("orders").First(buyer, buyer).First(seller, seller)
}
But I don't know of any Gorm helper function that actually does this. I also want this to be efficient because buyer and seller each have their ID primary keys.
How can I find an Order based on the Buyer / Seller like in the example above?
As an alternative, I am thinking of adding (buyer ID + seller ID) to make a custom order ID primary_key. This seems hacky though, as I feel like the whole point of relations is so I don't have to do something like this.
If you need to see the seller id and the buyer id in the orders table, include two fields for that in your orders struct, also you can use the foreignKey tag to populate those fields (by default they are populated with the primary id of the associated table record, you can use references tag as mentioned here to change that).
type Order struct {
ID int64 `json:"id" gorm:"primaryKey;autoIncrement:true"`
BuyerID int64 `json:"buyer_id" gorm:"index"`
SellerID int64 `json:"seller_id" gorm:"index"`
Buyer *Buyer `json:"buyer" gorm:"foreignKey:BuyerID"`
Seller *Seller `json:"seller" gorm:"foreignKey:SellerID"`
}
type Buyer struct {
ID int64 `json:"id" gorm:"primaryKey;autoIncrement:true"`
Name string `json:"name"`
}
type Seller struct {
ID int64 `json:"id" gorm:"primaryKey;autoIncrement:true"`
Name string `json:"name"`
}
As for the function to find orders given the buyer AND the seller you can use something like,
func findOrders(db *gorm.DB, buyerID int,sellerID int)[]Order{
orders := make([]Order,0)
db.Where("buyer_id=? AND seller_id=?",buyerID,sellerID).Find(&Order{}).Scan(&orders)
return orders
}
in contrast if you need to find orders for a given buyer OR the seller,
func findOrder(db *gorm.DB, buyerID int,sellerID int)[]Order{
orders := make([]Order,0)
db.Where("buyer_id=? OR seller_id=?",buyerID,sellerID).Find(&Order{}).Distinct().Scan(&orders)
return orders
}
The index tag covers the indexing requirement for orders table.
Use simple raw query builder for large queries
https://perfilovstanislav.github.io/go-raw-postgresql-builder/#simple-example
we have a problem to query our database in a meant-to-be fashion:
Tables:
employees <1-n> employee_card_validity <n-1> card <1-n> stamptimes
id id id id
employee_id no card_id
card_id timestamp
valid_from
valid_to
Employee is mapped onto Card via the EmployeeCardValidity Pivot which has additional attributes.
We reuse cards which means that a card has multiple entries in the pivot table. Which card is right is determined by valid_from/valid_to. These attributes are constrained not to overlap. Like that there's always a unique relationship from employee to stamptimes where an Employee can have multiple cards and a card can belong to multiple Employees over time.
Where we fail is to define a custom relationship from Employee to Stamptimes which regards which Stamptimes belong to an Employee. That means when I fetch a Stamptime its timestamp is distinctly assigned to a Card because it's inside its valid_from and valid_to.
But I cannot define an appropriate relation that gives me all Stamptimes for a given Employee. The only thing I have so far is to define a static field in Employee and use that to limit the relationship to only fetch Stamptimes of the given time.
public static $date = '';
public function cardsX() {
return $this->belongsToMany('App\Models\Tempos\Card', 'employee_card_validity',
'employee_id', 'card_id')
->wherePivot('valid_from', '>', self::$date);
}
Then I would say in the Controller:
\App\Models\Tempos\Employee::$date = '2020-01-20 00:00:00';
$ags = DepartmentGroup::with(['departments.employees.cardsX.stamptimes'])
But I cannot do that dynamically depending on the actual query result as you could with sql:
SELECT ecv.card_id, employee_id, valid_from, valid_to, s.timestamp
FROM staff.employee_card_validity ecv
join staff.stamptimes s on s.card_id = ecv.card_id
and s.stamptimes between valid_from and coalesce(valid_to , 'infinity'::timestamp)
where employee_id = ?
So my question is: is that database desing unusual or is an ORM mapper just not capable of describing such relationships. Do I have to fall back to QueryBuilder/SQL in such cases?
Do you suit your database model towards ORM or the other way?
You can try:
DB::query()->selectRaw('*')->from('employee_card_validity')
->join('stamptimes', function($join) {
return $join->on('employee_card_validity.card_id', '=', 'stamptimes.card_id')
->whereRaw('stamptimes.timestamp between employee_card_validity.valid_from and employee_card_validity.valid_to');
})->where('employee_id', ?)->get();
If your Laravel is x > 5.5, you can initiate Model extends the Pivot class I believe, so:
EmployeeCardValidity::join('stamptimes', function($join) {
return $join->on('employee_card_validity.card_id', '=', 'stamptimes.card_id')
->whereRaw('stamptimes.timestamp between employee_card_validity.valid_from and employee_card_validity.valid_to');
})->where('employee_id', ?)->get();
But code above is only translating your sql query, I believe I can write better if I know exactly your use cases.
i have relationship many to many table 'admins' , 'pjt_roles' with pjt_role_admin.
but,not working
i have 2 model
class Role
protected $table = 'pjt_roles';
public function Admin(){
return $this->belongsToMany(Admin::class',pjt_role_admin');
}
class Admin
public function Role(){
return $this->belongsToMany(Role::class,'pjt_role_admin');
}
and table pjt_role_admin have attribute
admin_id from table admins
role_id from table pjt_roles
Specify your pivot table in relationship. Default laravel assume admin_role as your pivot table because you have Admin and Role models
class Role
protected $table = 'pjt_roles';
public function Admin(){ // should be admins() for better readability
return $this->belongsToMany(Admin::class, 'pjt_role_admin');
}
class Admin
public function Role(){ // should be roles() for better readability
return $this->belongsToMany(Role::class, 'pjt_role_admin');
}
To determine the table name of the
relationship's joining table, Eloquent will join the two related model
names in alphabetical order. However, you are free to override this
convention. You may do so by passing a second argument to the
belongsToMany method.
Fetch Data
$admin = Admin::find(1);
$roles = $admin->Role; // should change to roles() in relationship for better readability
Save
$admin->Role()->attach($roleId);
details https://laravel.com/docs/5.4/eloquent-relationships#many-to-many
If the user requirement mock-up to something like that :
1-Company (id -name -etc)
and each Company has (Iron,Cement) entered in specific (month-year) .
The Iron has local sales and the Cement has Exporting.
Each Company has (Start balance) for (Iron,Cement) entered only once .
How to model something like that ?
Assuming I understood your domain one could do something like this:
Company(id, name, ...) // PK = id
Material(id, name) // PK = id
Entry(idCompany, idMaterial, month-year, production, localSales, exporting) // PK = idCompany, idMaterial, month-year
Initial(idCompany, idMaterial, date, balanceStart) PK = idCompany, idMaterial, date
If you create your tables this way you can use one table to store data for both cement and iron. You can then write a custom VIEW to display only relevant columns for each material.
For example, A Users entity has a friends property, how can I design this friends property, in my thought there are 2 ways:
friends property is a String with all usernames splitted by "," in this way it's hard to read and modify.
friends property is a Set like Set<Users>, but in this way I don't know how to write in entities?
Anyone knows the best practise?
This is covered Enterprise Model Patterns by Hay.
A party represents a person (or an organization):
Party
id
name
A party can have a relationship to another party, over a time period:
PartyRelationship
fromPartyId
toPartyId
fromDate
toDate nullable
A basic diagram:
Party -< PartyRelationship >- Party
Sample SQL:
insert into party values (1, 'Jerry');
insert into party values (2, 'Neil');
insert into partyRelationship values (1, 2, getDate(), null);
If a User can have multiple friends you could annotate your User entity like this:
#Entity
public class User
{
#Id
private Long id;
private String name;
#ManyToMany
#JoinTable(
name = "user_friends",
joinColumns =
{ #JoinColumn(
name = "user_id") },
inverseJoinColumns =
{ #JoinColumn(
name = "friend_id") })
private Set<User> friends;
}
This way a table will get created for User and a join table for the relationship between Users. The User table will have 2 columns, 'id' and 'name'. The user_friend table will have 2 columns, 'user_id' and 'friend_id'. The columns in user_friend are both foreign keys to the User table.