I have a users table that has location attributes and would like to create a model function that retrieves nearby users (within a given radius). Here is my model:
class User extends AppModel {
public function getNearbyUsers($id,$dist=10) {
return $this->query(...);
}
}
And here is my controller where I am trying to invoke the function:
class UsersController extends AppController {
public function getNearbyUsers($id) {
...
$this->User->getNearbyUsers($id)
...
}
}
However doing so results in: PHP Fatal error: Call to a member function getNearbyUsers() on a non-object
What am I doing wrong?
EDIT: nevermind, it is not complaining about that anymore. But it is throwing an SQL error and my model function is never actually being called. Upon further inspection on the mysql query log I see this:
Query SHOW TABLES FROM `xxx`
Query getNearbyUsers
Quit
Seems CakePHP is interpreting $this->User->getNearbyUsers as a literal query. So my question still remains: how to add custom functions to a model in Cake?
See http://book.cakephp.org/2.0/en/models/additional-methods-and-properties.html:
While CakePHP’s model functions should get you where you need to go,
don’t forget that model classes are just that: classes that allow you
to write your own methods or define your own properties.
Any operation that handles the saving and fetching of data is best
housed in your model classes. This concept is often referred to as the
fat model.
Model
class Example extends AppModel {
function getRecent() {
$conditions = array(
'created BETWEEN (curdate() - interval 7 day) and (curdate() - interval 0 day)'
);
return $this->find('all', compact('conditions'));
}
}
This getRecent() method can now be used within the controller.
Controller
$recent = $this->Example->getRecent();
There are a few additional items that needs to be in the code, or else you will get the non-object error.
In the App Model :
<?php
class Get extends AppModel {
public function getRecent() {
// $conditions = array(
// 'created BETWEEN (curdate() - interval 7 day)' .
// ' and (curdate() - interval 0 day))'
// );
// return $this->find('all', compact('conditions'));
}
}
In the app controller,
?php
class GetsController extends AppController {
public $uses = array('Get'); // Needed, or the error will appear.
public function Example () {
$this->Get->getRecent();
}
}
Had the same issue with Cake 1.3, using Plugins (Modules), and even if we had the model name unique in the whole app (some model names are used in more then one plugin) it only worked once I requested the Model in the controller's $uses array with it's plugin to, like this: 'Module1.A'
app/plugins/plugin1/controllers/a_controller.php:
class AController extends AppController {
// using simple array('A') worked fine for cake methods (find, query ...)
// but did not recognized the custom method
public $uses = array('Plugin1.A');
public function Example () {
$this->A->customMethod();
}
}
app/plugins/plugin1/models/a.php:
<?php
class A extends AppModel {
public function customMethod() {
// ...
}
}
Related
According to Yii 2 Rest APi documentation, I have a CountriesCountry that extends \yii\rest\ActiveController and a corresponding Countries model. This is the code for my Controller class.
<?php
namespace app\controllers;
class CountriesController extends \yii\rest\ActiveController{
public $modelClass = 'app\models\Countries';
public function actionIndex(){
}
public function actionView(){
}
public function actionCreate(){
}
public function actionUpdate(){
}
public function actionDelete(){
}
public function actionOptions(){
}
}
When I send a get request, it returns all the countries in my database.
My Question
is it possible to return my own result from action methods. Like in the actionIndex(), I will like to limit the result to 20 records. I did something like this but it is not working.
public function actionIndex(){
$model = Countries::find()->limit(20);
print_r($model);
}
I know that I can get all the countries from by database and loop through it and obtain only 20 results but I want to just query for 20 records from database.
Your class CountriesController that extends from \yii\rest\ActiveController automatically supports GET, PUT, POST calls etc. No need for actionIndex(), actionCreate(), etc if you just want regular REST functionality. Read about it in the Yii2 guide.
To limit the results you could just set another page size in your controllers afterAction-method. Add this to your controller. (I believe that 20 records is default of Pagination class, so if that is what you want you don't need this code at all. Just use the default functionality of yii/rest/ActiveController.)
public function afterAction($action, $result) {
if (isset($result->pagination) && ($result->pagination !== false)) {
$result->pagination->setPageSize(100);
}
return parent::afterAction($action, $result);
}
Suppose your api link is:
http://localhost/yii2-rest/api/country/?limit=15&order=id
Controller:
public function actionIndex(){
$model = Countries::find()
->orderBy($_GET['order'])
->limit($_GET['limit'])
->all();
return $model;
}
Take care about security!
You can get query string this way:
$limit = Yii::app()->getRequest()->getQuery('limit');
Straightway
//SELECT * FROM countries LIMIT 20
$countries= Countries::find()->limit(20)->all();
I encountered a strange behaviour which I do not understand when using type-hinting in a Laravel Controller. I have a Profile controller, which should call the same function of a service twice based on different parameters. However, when I call the function the second time, the result of the first call is changed.
Here is my code; Two users' profiles should be displayed on one page:
//use statements are omitted for brevity
class ProfileController extends Controller {
protected $compose_profile_service;
public function __construct(ComposeProfileService $compose_profile_service) {
$this->compose_profile_service = $compose_profile_service;
}
public function compare($user1, $user2) {
$user1_profile = $this->compose_profile_service->composeProfile($user1);
$user2_profile = $this->compose_profile_service->composeProfile($user2);
dd($user1_profile); //Problem: This dd() gives me the profile of user2 instead of user1
}
}
class ComposeProfileService {
public function composeProfile($user) {
//Get some stuff from the DB
$profile_data = $user->profile()->get();
return $profile_data;
}
}
Of course, if I dd() $user1 or $user2 above the respective call of the compose function, the right user is provided.
Could you provide an example usage. Description will be highly appreciated. I can not find a good example for it.
The Active Query represents a DB query associated with an Active Record class. It is usually used to override the default find() method of a specific model where it will be used to generate the query before sending to DB :
class OrderQuery extends ActiveQuery
{
public function payed()
{
return $this->andWhere(['status' => 1]);
}
public function big($threshold = 100)
{
return $this->andWhere(['>', 'subtotal', $threshold]);
}
}
If you worked before with Yii 1 then this is what replaces Yii 1.x Named Scopes in Yii2. All you have to do is to override the find() method in your model class to use the ActiveQuery class above :
// This will be auto generated by gii if 'Generate ActiveQuery' is selected
public static function find()
{
return new \app\models\OrderQuery(get_called_class());
}
Then you can use it this way :
$payed_orders = Order::find()->payed()->all();
$very_big_orders = Order::find()->big(999)->all();
$big_payed_orders = Order::find()->big()->payed()->all();
A different use case of the same ActiveQuery class defined above is by using it when defining relational data in a related model class like:
class Customer extends \yii\db\ActiveRecord
{
...
public function getPayedOrders()
{
return $this->hasMany(Order::className(),['customer_id' => 'id'])->payed();
}
}
Then you can eager load customers with their respective payed orders by doing :
$customers = Customer::find()->with('payedOrders')->all();
I have the following in my controller:
public function actionIndex() {
$userID = Yii::app()->user->getId();
$arNotifs = Notification::model()->getNotificationsByUserId($userID);
$this->render('index', array("arNotifications"=>$arNotifs, "userID"=>$userID));
}
I have the following in a file called notification.php in my models:
class Notification extends CActiveRecord {
// ------ BUNCH OF STUFF
public function getNotificationsByUserId($userId) {
$userId = (int) $userId;
$query = Yii::app()->db->createCommand();
$query->select('n.id, n.title, n.content, n.updated');
$query->from('hr4_notification_x_user nxu');
$query->join('hr4_notification n', 'nxu.notification = n.id');
$query->where('nxu.user=:userId', array(':userId' => $userId);
return $query->queryAll();
}
// ------ MORE STUFF
}
When I rem out the line
$arNotifs = Notification::model()->getNotificationsByUserId($userID);
and replace it with a static value it works fine. It seems that in my noob ways I am missing some vital step. The controller seems to have no idea what Notification is.
Thanks in advance
I believe the most elegant way to get your notifications on the controller would be something like:
$arNotifs = Yii::app()->user->model->notifications;
To achieve such, you might need to implement a getModel() method on your class that extends CWebUser. That method would return an instance of an user that extends CActiveRecord. Then your user model can have a relations() method, like the following:
class UserModel extends CActiveRecord {
public function relations() {
return array(
'notifications' => array(self::HAS_MANY, 'Notification', 'user'),
);
}
}
This will prevent you from writing that query and will make things more clear (on both, models and controller). If you will, read a bit about relations.
You cannot use the Notification model like this.
You can instantiate it with $notification = new Notification();
and then do a $notification->getNotificationsByUserId($userID);
However this would be not very good. I would move the notification code from the model to the User model.
This was you dont even need to pass the user ID.
Or maybe even better, if you make a component out of Notification and use it as a service.
I was creating a like system for my website. in this I wanted one user can only like one time for a post. and a post can be liked by many user. Also many user can like many post.
So if I guess it right, It is a many to many reletionship.
in this context,
I create the following table
... users table:
id
name
....
posts table :
id
post
...post_likes table
id
user id
poost_id
Now I am having the following model for
user :
class User extends SentryUserModel {
public function post_likes()
{
return $this->has_many('Post_like', 'id');
}
}
post :
class Post extends Eloquent {
public function post_likes()
{
return $this->has_many('Post_like', 'id');
}
}
post_like :
class Post_like extends Eloquent {
public function posts()
{
return $this->belongs_to('Post', 'post_id');
}
public function users()
{
return $this->belongs_to('User', 'user_id');
}
}
now when I am going to insert into the database (for post_likes table) I am getting an error called
Illuminate \ Database \ Eloquent \ MassAssignmentException
user_id
Also I want to know is there any way to inset into database like
$user->like()->save($user); ?????
Thank you in advance. Happy coding . \m/
I'll start with a basic issue, firstly you might want to make sure all your tables are lower case (still as a snake case as well), it's not required but it's ultimately how it's expected to be with Laravel so it makes life easier to keep with that. Also a note to the wise, like Class names, database tables are typically in the singular so user instead of users
Secondly yes you can do an insert with $user->post_likes()->save($debate); as your post_likes method on the user class returns has_many.
Thirdly, your design of the Post_like class is a bit off, you could be better off make it like so:
class PostLike extends Eloquent { // note that PostLikes is a more standard naming for a class, they should ideally be camel case names but with all capitals for words
protected $table = 'post_like'; // specifies the table the model uses
public function post() // this should be singular, the naming of a belngs_to method is important as Laravel will do some of the work for you if let it
{
return $this->belongs_to('Post'); // by naming the method 'post' you no longer need to specify the id, Laravel will automatically know from the method name and just adding '_id' to it.
}
public function users()
{
return $this->belongs_to('User');
}
}
Fourthly, your other classes could be better as:
class Post extends Eloquent {
public function post_likes()
{
return $this->has_many('PostLike'); // doesn't require you to specify an id at all
}
}
I can't exactly tell you why you're getting that mass assign error, your post is a bit garbled and doesn't look like you've included the code that actually causes the exception? I have a feeling though is that you're trying to do an insert for multiple database rows at one time but haven't defined a fillable array for PostLike such as with here: http://four.laravel.com/docs/eloquent#mass-assignment
According to Laravel 5:
User Model:
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model {
public function post_likes()
{
return $this->hasMany('App\PostLike');
}
}
Post Model:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model {
public function post_likes()
{
return $this->hasMany('App\PostLike');
}
}
PostLike Model:
namespace App;
use Illuminate\Database\Eloquent\Model;
class PostLike extends Model {
protected $table = 'post_like';
public function posts()
{
return $this->belongsTo('App\Post');
}
public function users()
{
return $this->belongsTo('App\User');
}
}
and if you want to save the post_like data then:
$inputs['post_id'] = 1;
$inputs['user_id'] = 4;
$post_like = PostLike::create($inputs);
Hope this helps!!