I have 2 tables vendors and partners
vendors table only stores the name of some company and partners table store users who work for some company. So the structure is somethign like this:
vendors
| id | name |
+------+---------------+
| 1 | Vendor-1 |
| 2 | Vendor-2 |
| 3 | Vendor-3 |
Partners
| id | user_name | password |vendor_id | is_owner | is_admin | is_technitian |
+----+------------+-----------+----------+------------+------------+---------------+
| 1 | abc | ^&ASKJHA | 1 | 1 | 1 | 0 |
| 2 | def | ^&ASKJHA | 2 | 1 | 1 | 0 |
| 3 | ghi | ^&ASKJHA | 1 | 0 | 1 | 0 |
| 4 | jkl | ^&ASKJHA | 3 | 1 | 1 | 0 |
| 5 | mno | ^&ASKJHA | 1 | 0 | 0 | 1 |
| 6 | pqr | ^&ASKJHA | 2 | 0 | 1 | 0 |
| 7 | stu | ^&ASKJHA | 1 | 0 | 0 | 1 |
| 8 | vwx | ^&ASKJHA | 2 | 0 | 0 | 1 |
| 9 | yz | ^&ASKJHA | 3 | 0 | 0 | 1 |
So as you can see above that One partner is the owner of any vendor and rest of them work as employees for the vendor.
I am working with Eloquent ORM and i have already defined Models for both Partner and vendor. I want to add an owner method in the Vendor Model so i can directly access the owner for any vendor Object. What i want to know is how do i relate this in my model defination. Is it do-able or do i need to make some changes in my database structure ?
class Vendor extends Model{
/**
* Get all the users for this vendor
*/
public function users(){
$this->hasMany(Partner::class);
}
public function owner(){
// how do i relate one owner from partner model who has is_owner == 1
}
}
Try with where()
public function owner(){
$this->hasOne(Partner::class)->where('is_owner', true);
}
You may have to specify the foreign key in the relationship.
Instead of defining a new relationship, and leading to duplicate code and breaking the DRY principle, make use of Local Scopes which comes out of the box in Eloquent ORM.
Local scopes allow you to define common sets of constraints that you may easily re-use throughout your application. For example, you may need to frequently retrieve all users that are considered "popular". To define a scope, simply prefix an Eloquent model method with scope.
Scopes should always return a query builder instance:
for instance:- define a function ScopeOwner() in your Vendor model such as:
public function scopeOwner($query)
{
return $query->where('is_owner', 1);
}
and then use it in your controller or implement it as follows:
$vendor->users()->owner()->get();
Once the scope has been defined, you may call the scope methods when querying the model. However, you do not need to include the scope prefix when calling the method. You can even chain calls to various scopes.
What's next, you could even use dynamic scopes to get even better code re-usage.
Sometimes you may wish to define a scope that accepts parameters. To get started, just add your additional parameters to your scope. Scope parameters should be defined after the $query parameter:
public function scopeOfType($query, $type)
{
return $query->where('is_owner', $type);
}
and consume it as follows:
$vendor->users()->ofType(0)->get();
for more information, check back to official docs: Eloquent ORM Scopes
Related
I have a table queue and I want to know what position or row number a user at.
queue table
----------------------------------------------------
| id | name | created_at | done |
+-----+--------+-------------------------+---------+
| 1 | John | 2020-10-17 01:08:59 | 1 |
| 2 | Jane | 2020-10-17 01:10:15 | 0 |
| 3 | Jess | 2020-10-17 01:18:15 | 0 |
| 4 | Joe | 2020-10-18 08:18:15 | 0 |
| 5 | Moe | 2020-10-18 11:18:15 | 0 |
----------------------------------------------------
is it possible to know the specific number of user in queue? for example Jess will return 3 because he's the 3rd user in the queue record.
edit: for example John is done in the queue, now Jess will become the 2nd in the queue.
I think I have a way:
the main idea is to get the count of the previous queue based on their id values,
and the trick is to use an alias to the main table so you can use that alias in your internal select.
$values = DB::table('queue', 'u1')
->select('u1.id',DB::raw("((SELECT count(*) from queue WHERE queue.id < u1.id)+1) rowNumber"))
->orderBy('u1.id')
->get();
Edit:
if you want to exclude done queue you should do it in the main and the internal select:
$values = DB::table('queue', 'u1')
->select('u1.id',DB::raw("((SELECT count(*) from queue WHERE (queue.id < u1.id)and(queue.is_done!=1) )+1) rowNumber"))
->where('u1.is_done','!=',1)
->orderBy('u1.id')
->get();
If you don't need to get that number directly with query you can do
search()
$collection->search(function ($item, $key) {
return $item->name == 'Jess';
});
Note. Also posted on Database Administrators
I have one table that records all sales and fourteen supplemental tables which contain extra information about a sale. The fourteen supplemental tables are for all intents and purposes the same. They were created long ago when the initial developer thought there would be more differences but actually now that the project has matured they are more similar than they are different. They are different however, and as such I need to keep them separate.
Current structure
Sales table
| id | customer_id | customer_ref | ... |
|---------|-------------|--------------------------------------|-----|
| 1237567 | 354 | a6143f8c-b679-47be-9bc0-52457842913c | ... |
| 1237568 | 867 | ref89b72 | ... |
| ... | ... | ... | ... |
Supplemental table 1 Class: App\SuppOne
| id | customer_id | customer_ref | ... |
|------|-------------|--------------------------------------|-----|
| 2857 | 10372 | 2016-07-01-ab5d09cc37ca | ... |
| 2858 | 354 | a6143f8c-b679-47be-9bc0-52457842913c | ... |
| ... | ... | ... | ... |
Supplemental table 2 Class: App\SuppTwo
| id | customer_id | customer_ref | ... |
|-------|-------------|--------------|-----|
| 90488 | 867 | ref89b72 | ... |
| 90489 | 1024 | 0000080992 | ... |
| ... | ... | ... | ... |
There are no foreign keys on the tables to join the sales table to the supplemental tables but there is a 'customer_id' and 'customer_reference' which are unique to both the sales tables and also the supplemental tables but they are not consistent. This is what is currently used to join the two as-and-when I need to get more information about a given sale.
I'm using Laravel 5.1 and a MySQL database and I'd like to add two fields to the sales table; supplemental_id and supplemental_type in order to quickly and efficiently create a polymorphic relation.
Desired structure
Sales table
| id | supplemental_id | supplemental_type | customer_id | customer_ref | ... |
|---------|-----------------|-------------------|-------------|--------------------------------------|-----|
| 1237567 | 2858 | App\SuppOne | 354 | a6143f8c-b679-47be-9bc0-52457842913c | ... |
| 1237568 | 90488 | App\SuppTwo | 867 | ref89b72 | ... |
| ... | ... | ... | ... | ... | ... |
I need to add these two fields to each of the sales records but I am unsure how to do this with raw SQL as I expect it would be much quicker than if done in a migration. I'd like to know how (if possible) in SQL, do I deal with the mapping from table_name to App\ClassName. There are about 1.5m records in the sales table and looping over them all will not take an insignificant amount of time.
something between the lines of.
It can potentially override the data, for particular sale record (ie. SuppFourteen overrides SuppOne data), but that's how you presented it in your question.
$fourteen_tables = [
'supplemental_table_1' => App\SuppOne::class,
// ...
];
foreach ($fourteen_tables as $table => $class) {
DB::table('sales_table')
->join($table, function ($join) use ($table) {
$join->on($table.'.customer_id', '=', 'sales_table.customer_id')
->on($table.'.customer_ref', '=', 'sales_table.customer_ref');
})
->update([
'sales_table.supplemental_id' => DB::raw($table.'.id'),
'sales_table.supplemental_type' => $class,
]);
}
I've looked a bunch of answers to this question here on SO and elsewhere but all I can track down is cases where people just want to find the highest id, the max dateCreated or the latest db entry but what I want to do is retrieve the latest object created that also matches another criteria. My domain class has the following properties: id, number, company, type, dateCreated and content. The company property can only be set to 'OYG' or 'BAW' and the number property is an auto incrementing int. What I want to do is retrieve the record with the highest number that also has its company property set to 'OYG' or 'BAW`.
So here's an example:
+----------------------------------------------------------+
| id | number | company | type | dateCreated | content |
+----------------------------------------------------------+
| 1 | 0 | OYG | TsAndCs | 15/09/2016 | stuff |
| 2 | 0 | BAW | TsAndCs | 15/09/2016 | stuff |
| 3 | 1 | OYG | TsAndCs | 16/09/2016 | stuff |
| 4 | 2 | OYG | TsAndCs | 17/09/2016 | stuff |
| 5 | 1 | BAW | TsAndCs | 16/09/2016 | stuff |
+----------------------------------------------------------+
I want to say def doc = Document.findByHighestNumberAndCompany('OYG') then it should bring back the object with id 4. def doc = Document.findByHighestNumberAndCompany('BAW') should bring back id 5's object, etc.
Any help would be appreciated. Thanks!
Despite Joshua Moore gave you a good solution, there is another simplier in one line.
MyDomain.findAllByCompany(company, [sort: 'number', order: 'desc', limit: 1])?.first()
Should be easy enough if you order by the number in descending order, and limit your results to one. So perhaps something like this?
String companyName = 'OYG'
def results = MyDomain.createCriteria().list() {
eq("company", companyName)
maxResults(1)
order("number", "desc")
}
println results[0].id // will print 4
Using this approach you could create a named query so you can pass the company name as a parameter.
I've got a permission/privileges - table looking like this:
+----+----------+----------+------+-------+
| id | name | usertype | read | write |
+----+----------+----------+------+-------+
| 1 | test | A | 0 | 0 |
| 2 | test | MU | 1 | 1 |
| 3 | test | U | 1 | 1 |
| 4 | apple | A | 1 | 1 |
| 5 | apple | MU | 1 | 0 |
| 6 | apple | U | 0 | 0 |
| 7 | flower | A | 0 | 0 |
| 8 | flower | MU | 0 | 0 |
| 9 | flower | U | 1 | 1 |
+----+----------+----------+------+-------+
there are 3 usertypes: A (admin), MU (maintenance user), U (standard user)
the usertypes are hierarchical: A > MU > U
(the usertypes are saved as CHAR(2) in the database, and unfortunately I can't change that)
now i want to build a query which implements the hierarchical logic of my usertypes.
e.g. usertype 'A' got no permission to read or write on stuff with the name 'test', thus usertypes 'MU' AND 'U' also should have no permission for that and their read = 1 and write = 1 should be ignored.
I know which usertype is currently logged in.
I somehow have to check for the minimum of read/write rights to the name for all hierarchical predecessors, i guess. but i don't know how to check that since usertype is not a number field.
this is what I've tried so far:
SELECT
name,
MIN(read),
MIN(write),
CASE
WHEN usertype = 'A' THEN 0
ELSE (CASE
WHEN usertype = 'WU' THEN 1
ELSE 2
END)
END userval
FROM
permissions
-- WHERE usertype <= :current_usertype
GROUP BY name
this seems to work, but i don't know how i can get my condition WHERE usertype <= :current_usertype working, so a usertype down in the hierarchy can't get more privileges on a name than a "higher" usertype.
any ideas?
thanks in advance!
This is how I solved my problem:
1. I added another table "permission_groups" to the database:
+----+----------+--------+
| id | usertype | value |
+----+----------+--------+
| 1 | A | 100 |
| 2 | MU | 20 |
| 3 | U | 10 |
+----+----------+--------+
2. Then I joined this table to my original table "permissions" which i showed in my question:
here i get the value of my "permission_groups" table with a subquery. this value symbolizes the hierarchical order of my different usertypes.
SELECT
perm.name,
MIN(perm.`read`),
MIN(perm.`write`),
group .value
FROM
permissions perm
LEFT JOIN permission_groups group ON group.usertype = perm.usertype
WHERE
group.value >= (SELECT value from permission_groups WHERE usertype = :current_usertype)
GROUP BY perm.name
:current_usertype is a PDO parameter in my case, which is replaced by the usertype of the current user.
I am setting up a database and am at a place where I am confusing myself on some many-to-many tables. I am looking for some advice on the best way to design this for performance and scalability.
I will lay out an example of my setup and what I am trying to do below.
I have the main object table...
Account
AccountID| AccountName
-----------------------
1 | First Account
...and then the child objects to be given permissions to.
Page
PageID | PageName
------------------
1 | First Page
Control
ControlID | ControlName
-----------------------
1 | First Control
MenuItem
MenuItemID | MenuItemName
-------------------------
1 | Menu Item 1
I have a permissions table for read/write etc...
Permission
PermissionID | PermissionName
------------------------------
1 | CanRead
2 | CanWrite
3 | CanDelete
So I'm trying to tie the permission table in a many-to-many between the main objects and the child objects. I will list below what I've come up with and why I don't think it's right.
One table to rule them all
PermissionAccount
AccountID | PermissionID | ControlID | MenuItemID | PageID
----------------------------------------------------------
1 | 2 | NULL | NULL | NULL
2 | NULL | 2 | NULL | NULL
*this solution is just ugly. There can be many MenuItemID's assigned to a single account
One table for every object
PermissionAccountControl
AccountID | ControlID | PermissionID
------------------------------------
1 | 1 | 1
1 | 2 | 1
PermissionAccountMenuItem
AccountID | MenuItemID | PermissionID
-------------------------------------
1 | 1 | 2
1 | 2 | 1
PermissionPage
AccountID | PageID | PermissionID
---------------------------------
1 | 1 | 3
1 | 2 | 1
I am leaning more toward option two. Any thoughts or suggestions are appreciated.
The latter is correct
This is 4th/5th normal form when designing a database
You could also use polymorphic association
+------------+------------+----------+--------------+
| AccountID | ObjectType | ObjectID | PermissionID |
+------------+------------+----------+--------------+
| 1 | Page | 1 | 1 |
| 1 | MenuItem | 1 | 2 |
+------------+------------+----------+--------------+
This method might be useful if you need to handle permissions for additional objects in your software at a later point of time. Then you won't have to create new tables or add new columns in existing tables if you use this table structure.