I've this
$reviewModel=Review::model()->findAll();
Now I want to perform a search operation on $reviewModel,say I want to search if a user of id 1 has posted a review or not. so is there any function provided by yii.
Note: I dont want to use
$reviewModel=Review::model()->findAll(array('condition'=>'')); As I
need all the reviews & then perform a search.
This is what I would recommend and is good design practice, also it will allow optimal performance from your database, + reduce the amount of queries. I would make your reviews have a relation with the user. As a user will post many reviews. so user HAS_MANY reviews. and Review and HAS_ONE user. so use that and create a foreign key to build the relationship.
So inside your user model (Relations) id have something like this:
'reviews' => array(self::HAS_MANY, 'Review', 'originator),
now inside your review model id have something like this:
'user' => array(self::BELONGS_TO, 'User', 'originator'),
Once you have done that all you would have to do is loop through all the users reviews. If its null, they don't have any reviews.
$user = User::model()->findbyPk(1);
and user->reviews would contain all the reviews for that user.
Not that I am aware of (if I understand your question correctly). Yii provides various ways to ask for what you want from the DB but not from an array of active record models in memory.
If you want to do it this way then get the array of data as you already did and then use PHP to iterate through the array of models looking for what you want.
You can filter through them manually. For example:
$reviewModel=Review::model()->findAll();
$matched = false;
foreach($reviewModel as $r) //cycle through each review
{
if($r->user == 1) //check if user is desired
{
$matched = true; //set matched as true
break; //stop searching, as we already found one
}
}
if you want to search for multiple things, you can use an array for matched and then check that array.
$reviewModel=Review::model()->findAll();
$wanted = array(0=>false,3=>false,6=>false,8=>false);
foreach($reviewModel as $r) //cycle through each review
{
if(isset($wanted[$r->user]) and !$wanted[$r->user]) //check if user is in wanted list and still false
{
$wanted[$r->user] == true; //set appropiate user to true
}
}
Related
Good day. I am building an API, in which I want to return some data. I have three tables
counsels
counsel_cases
analysis_sc
A section of the counsels table is shown below
A section of the counsel_cases is also shown below
Finally, a section of the analysic_sc is shown below.
I want that when a counsel is selected, through the counsel_id, I can fetch the cases belonging to the counsel from the counsel_cases, and then with that information, I want to be able to fetch the number of cases of such a counsel belonging to a legal head (area of practice) on the third table as shown in the design below.
How will that be possible. I have a relationship between counsel and counsel_cases though. Also, I have tried using a foreach loop as shown below, but I am unable to get the unique values of the legal_head.
public function getCounselPracticeAreas(Request $request)
{
$counsel_id = $request->route('counsel_id');
$cases = CounselCase::select('suit_number')->where('counsel_id', $counsel_id)->get();
$data = [];
foreach ($cases as $case) {
$values = AnalysisSc::select('legal_head')->where('suitno', $case->suit_number)->first();
array_push($data, $values);
}
return response()->json([
"message" => "successful",
"data" => $data
]);
}
However, this is the value I get
[![enter image description here][5]][5]
I want to get something like this:
$data : [
"legal_head" : [
"name" : "Criminal Law",
"count" : 2
]
]
Please, is this possible
I know this is quite long. And I hope I explained myself well. Thanks
you need to use SQL Joins to create a relationship between the tables and fetch the data
It looks like you're skipping using relationships in your models. Once you have your models set up, retrieving the data you need will be a lot easier with Laravel. You may want to separate your models/tables a little more and add relationships, like the following:
Models/tables:
Counsel (counsels)
id
name
LegalHead (legal_heads)
id
name
Case (cases)
id
suit_number
year
subject_matter
legal_head_id
case_counsels (This is not a model, just a relationship table)
id
case_id
counsel_id
appearing_for
role
Note: I wasn't sure how your data is structured. You can use this as start and adjust as necessary.
Relationships
The Many-Many relationship is suitable for the case counsels and you'll be able to add the role as a pivot(extra field) for the relationship.
https://laravel.com/docs/8.x/eloquent-relationships#many-to-many
Case
legal_head: belongsTo
counsels: belongsToMany with pivot for role
Counsel
cases: belongsToMany with pivot for role
Retrieving the Objects
When that is set up, you won't have to do as much query work. You can do this to get case data with counsels, legal_head and their role for each case:
$cases = Case::with('counsels','legal_head')->get();
And this to get the Legal Head names with the number of cases:
$legal_heads = LegalHead::withCount('cases')->get();
I assume that this should all be in one query in order to prevent duplicate data in the database. Is this correct?
How do I simplify this code into one Eloquent query?
$user = User::where( 'id', '=', $otherID )->first();
if( $user != null )
{
if( $user->requestReceived() )
accept_friend( $otherID );
else if( !$user->requestSent() )
{
$friend = new Friend;
$friend->user_1= $myID;
$friend->user_2 = $otherID;
$friend->accepted = 0;
$friend->save();
}
}
I assume that this should all be in one query in order to prevent
duplicate data in the database. Is this correct?
It's not correct. You prevent duplication by placing unique constraints on database level.
There's literally nothing you can do in php or any other language for that matter, that will prevent duplicates, if you don't have unique keys on your table(s). That's a simple fact, and if anyone tells you anything different - that person is blatantly wrong. I can explain why, but the explanation would be a lengthy one so I'll skip it.
Your code should be quite simple - just insert the data. Since it's not exactly clear how uniqueness is handled (it appears to be user_2, accepted, but there's an edge case), without a bit more data form you - it's not possible to suggest a complete solution.
You can always disregard what I wrote and try to go with suggested solutions, but they will fail miserably and you'll end up with duplicates.
I would say if there is a relationship between User and Friend you can simply employ Laravel's model relationship, such as:
$status = User::find($id)->friends()->updateOrCreate(['user_id' => $id], $attributes_to_update));
Thats what I would do to ensure that the new data is updated or a new one is created.
PS: I have used updateOrCreate() on Laravel 5.2.* only. And also it would be nice to actually do some check on user existence before updating else some errors might be thrown for null.
UPDATE
I'm not sure what to do. Could you explain a bit more what I should do? What about $attributes_to_update ?
Okay. Depending on what fields in the friends table marks the two friends, now using your example user_1 and user_2. By the example I gave, the $attributes_to_update would be (assuming otherID is the new friend's id):
$attributes_to_update = ['user_2' => otherID, 'accepted' => 0 ];
If your relationship between User and Friend is set properly, then the user_1 would already included in the insertion.
Furthermore,on this updateOrCreate function:
updateOrCreate($attributes_to_check, $attributes_to_update);
$attributes_to_check would mean those fields you want to check if they already exists before you create/update new one so if I want to ensure, the check is made when accepted is 0 then I can pass both say `['user_1' => 1, 'accepted' => 0]
Hope this is clearer now.
I'm assuming "friends" here represents a many-to-many relation between users. Apparently friend requests from one user (myID) to another (otherId).
You can represent that with Eloquent as:
class User extends Model
{
//...
public function friends()
{
return $this->belongsToMany(User::class, 'friends', 'myId', 'otherId')->withPivot('accepted');
}
}
That is, no need for Friend model.
Then, I think this is equivalent to what you want to accomplish (if not, please update with clarification):
$me = User::find($myId);
$me->friends()->syncWithoutDetaching([$otherId => ['accepted' => 0]]);
(accepted 0 or 1, according to your business logic).
This sync method prevents duplicate inserts, and updates or creates any row for the given pair of "myId - otherId". You can set any number of additional fields in the pivot table with this method.
However, I agree with #Mjh about setting unique constraints at database level as well.
For this kind of issue, First of all, you have to enjoy the code and database if you are working in laravel. For this first you create realtionship between both table friend and user in database as well as in Models . Also you have to use unique in database .
$data= array('accepted' => 0);
User::find($otherID)->friends()->updateOrCreate(['user_id', $otherID], $data));
This is query you can work with this . Also you can pass multiple condition here. Thanks
You can use firstOrCreate/ firstOrNew methods (https://laravel.com/docs/5.3/eloquent)
Example (from docs) :
// Retrieve the flight by the attributes, or create it if it doesn't exist...
$flight = App\Flight::firstOrCreate(['name' => 'Flight 10']);
// Retrieve the flight by the attributes, or instantiate a new instance...
$flight = App\Flight::firstOrNew(['name' => 'Flight 10']);
use `firstOrCreate' it will do same as you did manually.
Definition of FirstOrCreate copied from the Laravel Manual.
The firstOrCreate method will attempt to locate a database record using the given column / value pairs. If the model can not be found in the database, a record will be inserted with the given attributes.
So according to that you should try :
$user = User::where( 'id', '=', $otherID )->first();
$friend=Friend::firstOrCreate(['user_id' => $myId], ['user_2' => $otherId]);
It will check with both IDs if not exists then create record in friends table.
I have a simple DB with multiple tables and relationships, ie:
Article - Category
User - Group
etc...
I have implemented SoftDelete behavior where there is a Active column and if set to 0, it is considered deleted.
My question is simple.
How to i specify in as few places as possible that i only want load Articles that belong to Active categories.
I have specified relationships and default scopes (with Active = 1) condition.
However, when i do findAll(), it returns those Articles that have Active = 1, even if the category it belongs to is Active = 0....
Thank you
Implementation so far:
In base class
public function defaultScope()
{
return array('condition' => 'Active = 1');
}
in model:
'category' => array(self::BELONGS_TO, 'Category', 'CategoryID'),
'query':
$data = Article::model()->findAll();
MY SOLUTION
So i decided, that doing it in framework is:
inneficient
too much work
not good as it moves business logic away from database - this is fairly important to save work later on when working on interfaces/webservices and other customizations that should be part of the product.
Overall lesson: Try to keep all business logic as close to database as possible to prevent disrepancies.
First, i was thinking using triggers that would propagate soft delete down the hierarchy. However after thinking a bit more i decided not to do this. The reason is, that this way if I (or an interface or something) decided to reactivate the parent records, there would be no way to say which child record was chain-deleted and which one was deleted before:
CASE:
Lets say Category and Article.
First, one article is deleted.
Then the whole category is deleted.
Then you realize this was a mistake and you want to undelete the Category. How do you know which article was deleted by deleting category and which one should stay deleted? Yes there are solutions, ie timestamps but ...... too complex, too easy to break
So my solution in the end are:
VIEWS. I think i will move away from yii ORM to using views for anything more complex then basic things.
There are two advantages to this for me:
1) as a DBA i can do better SQL faster
2) logic stays in database, in case the application changes/another one is added, there is no need to implement the logic in more then one places
You need to specify condition when you are using findAll method. So You should use CDbCriteria for this purpose:
$criteria=new CDbCriteria;
$criteria->with = "category";
$criteria->condition = "category.Active = 1"; //OR $criteria->compare('category.active', 1 true);
$data = Article::model()->findAll($criteria);
You should also have a defaultScope in your Article model, condition there should add category.Active = 1 or whatever your relation is named.
public function defaultScope()
{
return array('condition' => 't.Active = 1 AND category.Active = 1');
}
I don't remember by now but it might be you have to specify the relation:
return array(
'with' => array("category" => array(
'condition'=> "t.Active = 1 AND category.Active = 1",
)
);
I have setup Kohana models with relationships define (1:* and : using through) but wanted to know the best way to use them in WHERE statements.
Example: I have a User model and a Post model. Post has a foreign key (userID) to User
I have setup a $_has_many relationship on the User model with an alias user_created
I know I can use the actual field userID, but I want to be able to do something like this
$user->where('user_created', 'IS', NULL);
This would also be handy for checking whether a many-to-many with something like this
'm2m_relation_count', '>', 0
Is this possible? Thanks!
You can do this with the ORM in Kohana in the following way.
$user->user_created->find_all();
This will result in all Posts the User created.
If you want to check if the User has created posts you just use the same function and do a count() on the result and check if it is greater than 0.
I don't think you can use the alias in a where clause itself. You have to call it from the ORM object.
Many-to-many relations work the same way.
1) There is no quick method to get all users without posts. Id prefer to add special column post_count and increment it after posting.
2) You can check user for m:m relationship with has()/has_any() methods:
if ($user->has('roles')) {
// user has at least one role
}
if ($user->has('roles', $roleId) {
// user has role with id=$roleId
}
if ($user->has('roles', array($roleId1, $roleId2))) {
// user has both $roleId and $roleId2 roles
}
if ($user->has_any('roles', array($roleId1, $roleId2))) {
// user has on of the $roleId1 or $roleId2
}
Using CakePHP 2.2, I am building an application in which each client has it's own "realm" of data and none of the other data is visible to them. For example, a client has his set of users, courses, contractors and jobs. Groups are shared among clients, but they cannot perform actions on groups. All clients can do with groups is assign them to users. So, an administrator (using ACL) can only manage data from the same client id.
All my objects (except groups, of course) have the client_id key.
Now, I know one way to get this done and actually having it working well, but it seems a bit dirty and I'm wondering if there is a better way. Being early in the project and new to CakePHP, I'm eager to get it right.
This is how I'm doing it now :
1- A user logs in. His client_id is written to session according to the data from the user's table.
$user = $this->User->read(null, $this->Auth->user('id'));
$this->Session->write('User.client_id', $user['User']['client_id']);
2- In AppController, I have a protected function that compares that session id to a given parameter.
protected function clientCheck($client_id) {
if ($this->Session->read('User.client_id') == $client_id) {
return true;
} else {
$this->Session->setFlash(__('Invalid object or view.'));
$this->redirect(array('controller' => 'user', 'action' => 'home'));
}
}
3- Im my different index actions (each index, each relevant controller), I check the client_id using a paginate condition.
public function index() {
$this->User->recursive = 0;
$this->paginate = array(
'conditions' => array('User.client_id' => $this->Session->read('User.client_id'))
);
$this->set('users', $this->paginate());
}
4- In other actions, I check the client_id before checking the HTTP request type this way.
$user = $this->User->read(null, $id);
$this->clientCheck($user['User']['client_id']);
$this->set('user', $user);
The concept is good - it's not 'dirty', and it's pretty much exactly the same as how I've handled situations like that.
You've just got a couple of lines of redundant code. First:
$this->Auth->user('id')
That method can actually get any field for the logged in user, so you can do:
$this->Auth->user('client_id')
So your two lines:
$user = $this->User->read(null, $this->Auth->user('id'));
$this->Session->write('User.client_id', $user['User']['client_id']);
Aren't needed. You don't need to re-read the User, or write anything to the session - just grab the client_id directly from Auth any time you need it.
In fact, if you read http://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#accessing-the-logged-in-user it even says you can get it from outside the context of a controller, using the static method like:
AuthComponent::user('client_id')
Though it doesn't seem you'll be needing that.
You could also apply the client_id condition to all finds for a Model by placing something in the beforeFind function in the Model.
For example, in your User model, you could do something like this:
function beforeFind( $queryData ) {
// Automatically filter all finds by client_id of logged in user
$queryData['conditions'][$this->alias . '.client_id'] = AuthComponent::user('client_id');
return $queryData;
}
Not sure if AuthComponent::user('client_id') works in the Model, but you get the idea. This will automatically apply this condition to every find in the model.
You could also use the beforeSave in the model to automatically set that client_id for you in new records.
My answer may be database engine specific as I use PostgreSQL. In my project I used different schema for every client in mysql terms that would be separate database for every client.
In public schema (common database) I store all data that needs to be shared between all clients (objects that do not have client_id in your case), for example, variable constants, profile settings and so on.
In company specific models I define
public $useDbConfig = 'company_data';
In Controller/AppController.php beforeFilter() method I have this code to set schema according to the logged in user.
if ($this->Session->check('User.Company.id')) {
App::uses('ConnectionManager', 'Model');
$dataSource = ConnectionManager::getDataSource('company_data');
$dataSource->config['schema'] =
'company_'.$this->Session->read('User.Company.id');
}
As you see I update dataSource on the fly according to used company. This does exclude any involvement of company_id in any query as only company relevant data is stored in that schema (database). Also this adds ability to scale the project.
Downside of this approach is that it creates pain in the ass to synchronize all database structures on structure change, but it can be done using exporting data, dropping all databases, recreating them with new layout and importing data back again. Just need to be sure to export data with full inserts including column names.