Gii doesn't recognize many-to-many relationship to itself? - sql

So I am trying to implement a friendlist, the above is the SQL diagram I made for my simple project and after generating the Models. I realized there was something wrong with the way Gii generated the model.
I wanted to make a many-to-many relationship with User to itself, but this is what I got:
class User {
...
public function getPosts()
{
return $this->hasMany(Post::className(), ['userId' => 'id']);
}
}
class Friend {
...
public function getFriend()
{
return $this->hasOne(Member::className(), ['id' => 'friendId']);
}
}
The User class doesn't have any relationship with itself, I expected something like getUsers() inside of User, but it didn't generate it. I initially thought about not making a model with the junction table, but I did so just to see what would happen. I don't think I need it. So I am not sure how to do this correctly? Do I need to get rid of my Junction Table Models and Do I need to make the relationship between User to itself and User to Message manually? I thought about doing a many-to-many in User and Message and a many-to-many in User for User. Is this the right thing? Tell me if I am wrong. Thank you.

You are on a true way. You need a junction table for implementing your goal. Easily as you done this, you must define two model: User and Friend. Now on your User model at first you must define a relation for get the list of all friends, Suppose call it getFriendsLists:
public function getFriendsLists()
{
return $this->hasMany(Friend::className(), ['userId' => 'id']);
}
This relation says that "Get me all account that are connected with me, i.e. if my id is 102, this relation return all record of friend table that their userIds are 102". Well, now we get all friends with a relation on User model, let call him getFriends:
public function getFriends()
{
return $this->hasMany(User::className(), ['friendId' => 'id']
->via('friendsList');
}
Notice that 'friendsList' as is a parameter of via method, is our predefined relation on top of this answer. Now easily you can get all account that are friends of our example (User with id 102):
public FriendController extends Controller
{
// Some code goes here!
public function actionFriendList($id)
{
$user = User::findOne($id);
$friends = $user->friends;
return $this->render('friend-list', ['friendsArray' => $friends]);
}
}
And use them on your friend-list view file as $friendsArray variable. Extra note that $user->friends use friends relation that you defined on User model with getFriends method.

Related

How to get a column relationship based on field

I have a chat table that both a user and admin can chat the table is defined as follow:
id, from_id, to_id, message, is_from_admin.
what I want is, if the is_from_admin is true laravel should use the admin table at sql level for the from. otherwise it should use the user table for from and same applies to the to field. Thanks
If you have the chance, I'd rework the table a bit and name it like so:
id, from_user_type, from_user_id, to_user_id, message
The pair from_user_type and from_user_id can be used to creat a custom polymorphic relation ("type" refers to the model/table name, and "id" refers to the id of a row in this table) as seen here: https://laravel.com/docs/9.x/eloquent-relationships#one-to-many-polymorphic-relations .
If you also want to send admin-to-admin, you should also add to_user_type, to_user_id so you can create a polymorphic relationship on the receiving side as well.
The polymorphic relation will look something like this:
class ChatMessage
{
public function fromUser()
{
// This function should automatically infer `from_user_type` and `from_user_id`
// from this function name.
return $this->morphTo();
}
}
class AdminUser
{
public function chatMessages()
{
return $this->morphMany(ChatMessage::class, 'fromUser');
}
}
Laravel can not solve what you are doing, which is a polymorphic relationship, based on a boolean. Theoretically you could bind the polymorphic class definition to 0 or 1, but this is a hack at best. Alternatively you could rewrite your table structure to support polymorphic relations.
Instead i would say you achieve something that is working, with what you have. Create two relationships combined with some logic in an accessor. Create a relationship for the admin and for the user.
Chat extends Model
{
public function fromAdmin()
{
return $this->belongsTo(Admin::class, 'from_id')->where('is_from_admin', true);
}
public function fromUser()
{
return $this->belongsTo(User::class, 'from_id')->where('is_from_admin', false);
}
}
Now create the accessor on the Chat model, using your new relationships.
public function getFromAttribute()
{
return $this->fromAdmin ?? $this->fromUser;
}
With this approach, you should be able to access the attribute like this.
Chat::find(1)->from; // either a user or admin based on the data.

How to define a hasMany far relationship

Suppose you have
Facility 1------* Section 1------* Session *------1 Event
I.e., a facility has many sections; each section can hold many sessions; each session belongs to an event.
How can I define this as a relationship in the Facility model to retrieve all unique instances of Event that a facility is hosting? I have tried this:
class Facility extends Eloquent\Model {
public function events() {
return $this->hasMany('Event')
->join('session', 'session.event_id', '=', 'event.id')
->join('section', 'section.id', '=', 'session.section_id')
->join('facility', 'facility.id', '=', 'section.facility_id');
}
}
I don't know if I'm very close with that; Laravel adds a constraint implicitly ("events"."facility_id" in (...)) and everything gets messed up.
What is the proper way of doing this?
I would avoid putting any logic in a relationship method on your models. Instead use eloquent to load your relationships, either one after the other or eager load into the Facility model. The latter being preferable.
class Facility extends Eloquent\Model
{
public function sections()
{
return $this->hasMany('Section');
}
}
Just make sure each of the models has the correct relationship set up so you can chain an eager load. Such as:
class Section extends Eloquent\Model
{
public function sessions()
{
return $this->hasMany('Session');
}
}
Then when you come to load the relationships you can use dot notation to eager load them.
$facility = Facility::with('sections.sessions.event')->get();
$facility will now contain a Facility model with nested relationships. You can use some of the different array helpers to extract/pluck all of the events.
This is the closer i've got to my initial purpose:
I created an SQL view:
CREATE VIEW facility_events AS
SELECT DISTINCT ON (e.id) e.id,
e.name,
e.created_at,
e.updated_at,
f.id AS facility_id
FROM events e
JOIN sessions s ON e.id = s.event_id
JOIN sections_extended fse ON s.section_id = fse.id
JOIN facilities f ON fse.root_facility_id = f.id;
Then I create the corresponding FactoryEvent Eloquent model and, finally, in my class Facility:
public function events() {
return $this->hasMany('App\FacilityEvent');
}
I look forward to see Laravel-only solutions for this. In other frameworks, such as Yii, I have been able to do things like that without the need to work on the database directly.

Lithium Framework Architecture - Call One Controller from Another

I'm working on a web app using the Lithium Framework with a MongoDB database.
On one page of the application - I want to display data from multiple object types. I understand the concept of relationships (i.e. belongsTo, hasMany, etc.) between models. But, my questions has to do with Controller relationships.
For example, assume I have two objects named "People" and "Companies". I want to show specific information about Companies on a "people" view. I have done the following:
1) In the "People" model, I've added the following line:
public $belongsTo = array('Companies');
2) In the "PeopleController" file, I've also included a reference to the Companies Model, such as:
use app\models\Companies;
Now, within the PeopleController, I want to call a method in the CompaniesController file.
Do I access this by directly calling the CompaniesController file? Or, do I have to go thru the Company model.
In either case, I'll need help with the syntax. I'm having rouble figuring out the best way this should be called.
Thanks in advance for your help!
You should rethink your structure - you controller method should really grab all the resources you need for that view, it doesn't matter what they are.
So if you have a url '/people/bob' and you want to get the company data for Bob just add that to the view method of your People controller. Something like
People::first(array('conditions' => array('name' => 'Bob'), 'with' => 'Companies'));
You could instantiate a CompaniesController (maybe passing in $this->request to the 'request' option in the process) and then call the method in it. However, a better way to organize it is to move the common functionality from CompaniesController to Companies and call it from both places.
use app\models\Companies does not really make a "reference." It simply indicates that Companies really means app\models\Companies. I think an "alias" is a better way to think of it. See http://php.net/manual/en/language.namespaces.importing.php.
Example:
// in app/models/Companies.php
namespace app\models;
class Companies extends \lithium\data\Model {
public static function doSomething() {
// do something related to companies.
}
}
// in app/controllers/CompaniesController.php
namespace app\controllers;
use app\models\Companies;
class CompaniesController extends \lithium\action\Controller {
public function index() {
$result = Companies::doSomething();
return array('some' => 'data', 'for' => 'the view');
}
}
// in app/controllers/PeopleController.php
namespace app\controllers;
use app\models\Companies;
class PeopleController extends \lithium\action\Controller {
public function index() {
$result = Companies::doSomething();
return array('some' => 'data', 'for' => 'the view');
}
}

CWebUser and CUserIdentity

I'm building an authentication module for my application and I don't quite understand the relation between CWebUser and CUserIdentity.
To set the user id to Yii::app()->user->id I have to do that in my UserIdentity class and create a method:
public function getId() {
return $this->_id;
}
But to set isAdmin to Yii::app()->user->isAdmin I have to create a method in my WebUser class:
function getIsAdmin() {
$user = $this->loadUser(Yii::app()->user->id);
return intval($user->user_level_id) == AccountModule::USER_LEVEL_ADMIN;
}
Why can't I just create the methods the UserIdentity class? What is the division of labour here?
The UserIdentity (UI) class is like an ID card, where as the WebUser class is the actual person plus everything you know about them.
The UI class gives you authentication via database, webservices, textfile, whatever. It lets you know what the key attributes are and allows you to manipulate them. The user however can give you more information about what they're allowed to do, there names, granular permissions and such.
OK, end metaphor
The UI class holds the key information, so when asking for the users ID it will refer to the User Identity class to get the Identifier for the user.
Anything that isn't related to identifying or authenticating a user is in the WebUser class
Clear it up at all?
Your example
You gave the getId function as an example, but that can be created on WebUser to override the default, which is to pull from the state.
So not sure what you mean here.
I like how the accepted answer used real life examples to make it easier to understand. However, I also like how Chris explained it here with example.
User information is stored in an instance of the CWebUser class and
this is created on application initialisation (ie: when the User first
connects with the website), irrespective of whether the user is logged
in or not. By default, the user is set to “ Guest”. Authentication is
managed by a class called CUserIdentity and this class checks that the
user is known and a valid user. How this validation occurs will depend
on your application, perhaps against a database, or login with
facebook, or against an ldap server etc...
And what is the benefit of using all those classes? I can do everything just by User model. If I set scenario "login", password will be checked during validation. If validation is OK, I can set to session my own variable like this:
$model = new User("login");
$model->attributes = $_POST["User"];
if ($model->validate())
{
Yii::app()->session["currentUser"] = $model;
}
else
{
// .. show error
unset(Yii::app()->session["currentUser"]);
}
In User model I have then static methods to check this variable
public static function isGuest()
{
return isset(Yii::app()->session["currentUser"]);
}
public static function getCurrent()
{
return Yii::app()->session["currentUser"];
}
And I can call it very shortly:
User::isGuest();
$model = User::getCurrent();
// instead of writing this:
Yii::app()->user->isGuest;
So why should I use so complicated hierarchy of classes that is suggested by Yii? I never understood it.

Fluent HNibernate Mapping Property Issue

i have two entities one called User and another called Membership which has a one to many mapping from User to Membership. I need to add a property on my User entity called CurrentMembership which gets the latest Membership row (ordered by the property DateAdded on the Membership Entity). I'd appreciate it if someone could show me how this can be done.
Thanks
I don't think the property needs to be mapped with Fluent NHibernate unless you are planning on storing it in the database, which doesn't necessarily sound like a good idea to me. The following code is more than likely all you need:
public class User
{
private IList<Membership> _Membership = new List<Membership>();
public IList<Membership> Memberships
{
get { return _Membership; }
}
public Membership CurrentMembership
{
get
{
return Memberships
.OrderByDescending(x => x.DateAdded).FirstOrDefault();
}
}
}