CakePHP - Best way to Call API in controller? - api

In Message Model, I have
id/Subject/Comment/SenderID/RecipientID
So in Message Controller
public function index(){
$msgs = $this->Message->find();
$this->set('msgs', $msgs);
}
In Message view
foreach ($msgs as $msg)
echo ...
endforeach
BUT instead of outputting sender ID and recipient ID, I want to be able to get the username via an API which has already been set up, http://domain.com/userid/1, it will return the username in a json.
I know it's a bad practice to do this in view, but can you suggest how should I do this in controller?

Create a model for your API calls, tell cake not to look for a db table for it and have the function with the logic to pull the username there.
App::uses('AppModel', 'Model');
class MyAPI extends AppModel {
public $useTable = false;
public function getUserNameForID(Int $id = null) {
//Your logic here
return $userName;
}
}
Then in your controller you need to load the MyAPI model and pass it your data recursivly
$this->loadModel('MyAPI');
foreach (...) {
...
$userName = $this->MyAPI->getUserNameForID($userID);
...
}

Related

Symfony 3 get current user inside entity

I was wondering if there is a way that i can initialize the property owner with an entity User of FOSUserBundle so that it contains the user who created the Post
I want to do this inside the constructor as shown below.
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="post")
* #ORM\Entity(repositoryClass="AppBundle\Repository\PostRepository")
*/
class Post
{
/* here are defined some attributs */
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="posts")
* #ORM\JoinColumn(name="owner", referencedColumnName="id")
*/
private $owner;
public function __construct()
{
$this->owner = /* get current user */ ;
}
}
Is there a way to do this by replacing the comment in the constructor with something ?
Thank you for your answers
No, there isn't. [*]
There are at least two ways to deal with this:
Create your Post entities through a factory service which populates the
owner property:
namespace My\Bundle\EntityFactory;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use My\Bundle\Entity\Post;
class PostFactory
{
private $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function createPost()
{
$user = $this->tokenStorage()->getToken()->getUser();
$post = new Post($user);
}
}
(for this example, you will have to modify your Post constructor to
accept the owner as a parameter)
In services.yml:
services:
post_factory:
class: My\Bundle\EntityFactory\PostFactory
arguments: [#security.token_storage]
To create an entity from your controller:
$post = $this->container->get('post_factory')->createPost();
If you can tolerate that the owner will only be set once you persist the
entity, you can use a doctrine event listener:
namespace My\Bundle\EventListener;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use My\Bundle\Entity\Post;
class PostOwnerAssignmentListener
{
private $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function prePersist(LifecycleEventArgs $event)
{
$entity = $event->getEntity();
if ($entity instanceof Post && !$entity->getOwner()) {
$entity->setOwner($this->tokenStorage->getToken()->getUser());
}
}
}
In services.yml:
services:
post_owner_assignment_listener:
class: My\Bundle\EventListener\PostOwnerAssignmentListener
arguments: [#security.token_storage]
tags:
- { name: doctrine.event_listener, event: prePersit }
The advantage here is that the owner gets assigned no matter how and where
the Post is created.
[*]: Well, technically with the default app.php you could access the
kernel by declaring global $kernel; in your constructor and go from there,
however this is very strongly discouraged and may break in strange and subtle
ways.
I think you are way over-complicating this issue. When you create a new Post in your controller, either in the controller or in the repository do something like this:
use AppBundle\Entity\Post; //at top of controller
$em = $this->getDoctrine()->getManager();
$user = $this->container->get('security.token_storage')->getToken()->getUser();
$post = new Post();
$em->persist( $post );
$post->setOwner( $user );
// set other fields in your post entity
$em->flush();
For Symfony 4+ with Autowiring and Entity event listener:
In /EventListener/PostPrePersistListener.php:
namespace App\EventListener;
use App\Entity\Post;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class PostPrePersistListener
{
private $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function prePersist(Post $post, LifecycleEventArgs $event)
{
$post->setOwner($this->tokenStorage->getToken()->getUser());
}
}
In services.yaml:
services:
App\EventListener\PostPrePersistListener:
autowire: true
tags:
- { name: doctrine.orm.entity_listener, entity: 'App\Entity\Post', event: prePersist }
Modifying services.yaml is required as Symfony cannot know that this custom service is tagged to hook on doctrine.event_listener
This works at Entity-level as asked, to ensure Controller do not handle the owner value.

How to create a row for user in another related table when the user is created in laravel?

I need to create a row in a related table. This is a hasOne and belongsTo relationships.
A user hasOne profile
A profile belongsTo user
I want to create a profile row once the user has been created.
public function store(UserRequest $request)
{
$user= new User();
User::create($request->all());
Profile::create([
'user_id' => $user->user_id,
]);
return redirect()->route('users.index')
}
you can use events(observer) in laravel for doing your work when User created in any where of your project.
https://laravel.com/docs/5.3/eloquent#observers
Use Model Observers.
You can declare an observer to created event, and create the profile there.
public function creating($model)
{
// Create your related profile.
// Note that profile relationship must be defined at your model class
$model->profile()->create();
return true;
}
This observer may be created either on a Model Observer Class, or declared on model boot.
Try like this:
public function store(UserRequest $request)
{
$user = User::create($request->all());
$user->profile()->create([]);
return redirect()->route('users.index')
}
You have to have hasOne relation method in user's class:
public function profile()
{
return $this->hasOne(Profile::class);
}
This for update is not working; Is there a solution
public function update(Request $request, $id)
{
$input = $request->all();
$employee = Employee::findorfail($id);
$update = $employee->update($input);
$employee->personaldetail()->update(['employee_id' => $employee->employees_id,]);
return redirect()->route('employees.index')->with('message','Employee has been updated');
}

joomla mvc model results where ate they called?

I'm confused how to project models result in view in joomla 2.5
I have the controller which is initializing the model
class FrontpageMyComponentControllerItem extends JControllerLegacy
{
private $id;
public function display($cachable = false, $urlparams = array())
{
// Initialise variables.
$jinput = JFactory::getApplication()->input;
$this->id = $jinput->get('id');
$cachable = true;
$model = $this->getModel('item');
$result_in_view = $model->Item('23'); //$id what I get
// Set the default view name and format from the Request.
$viewName = $jinput->get('view', 'item');
$jinput->set('view', $viewName);
return parent::display($cachable, $safeurlparams);
}
}
now how do I have the result in my view?
In most components data (results) are being retrieved from Model by the View.
I'm not really sure why, but I guess it's to give more power to Views.
FrontpageMyComponentViewItem extends JViewLegacy
{
/** #var array Data are stored here */
public $items;
public function display($tpl = null)
{
/** Retrieve data from Model */
$items = $this->get('Items');
// Check for errors.
if (count($errors = $this->get('Errors')))
{
// Raise an error
JError::raiseWarning(500, implode("\n", $errors));
return false;
}
// Assign data, so layout can access these
$this->items =& $items;
parent::display($tpl);
}
}
This is different than usual MVC implementations where Controller is retrieving data from Model and passing it to View.
If you'd like to see example of this in Joomla, look at MediaController in Joomla 2.5.
Sometimes the View may not be necessary (output is in JSON) and then you may retrieve data inside controller and output it instantly using echo like in search suggestions
There are new MVC (vs legacy) classes in Joomla, but core components still don't use them.

UserIdentity and session issue

I have the following class. But when I try to access the Yii::app()->user->realName; it generates an error.
I can't understand it all. please help!
Following code is the code of my UserIdentity class.
<?php
/**
* UserIdentity represents the data needed to identity a user.
* It contains the authentication method that checks if the provided
* data can identity the user.
*/
class UserIdentity extends CUserIdentity {
public $id, $dmail, $real_name;
/**
* Authenticates a user.
* The example implementation makes sure if the username and password
* are both 'demo'.
* In practical applications, this should be changed to authenticate
* against some persistent user identity storage (e.g. database).
* #return boolean whether authentication succeeds.
*/
public function authenticate() {
$theUser = User::model()->findByAttributes(array(
'email' => $this->username,
// 'password' => $this->password
));
if ($theUser == null) {
$this->errorCode = self::ERROR_PASSWORD_INVALID;
} else {
$this->id = $theUser->id;
$this->setState('uid', $this->id);
// echo $users->name; exit;
// $this->setState('userName', $theUser->name);
$this->setState("realName",$theUser->fname .' '. $theUser->lname);
$this->errorCode = self::ERROR_NONE;
}
return!$this->errorCode;
}
}
?>
You need to extend the CWebUser class to achieve the results you want.
class WebUser extends CWebUser{
protected $_realName = 'wu_default';
public function getRealName(){
$realName = Yii::app()->user->getState('realName');
return (null!==$realName)?$realName:$this->_realName;
}
public function setRealName($value){
Yii::app()->user->setState('realName', $value);
}
}
You can then assign and recall the realName attribute by using Yii::app()->user->realName.
The protected $_realName is optional, but allows you to define a default value. If you choose not to use it, change the return line of the getRealName method to return $realName.
Place the above class in components/WebUser.php, or anywhere that it will be loaded or autoloaded.
Change your config file to use your new WebUser class and you should be all set.
'components'=>
'user'=>array(
'class'=>'WebUser',
),
...
),

How to set default action dynamically in Yii

i want to change default action of a controller depends on which user is logged in.
Ex. There are two users in my site : publisher and author and i want to set publisher action as default action when a publisher is logged in, and same for author.
what should i do? when can I check my roles and set their relevant actions?
Another way to do this would be setting the defaultAction property in your controller's init() method. Somewhat like this:
<?php
class MyAwesomeController extends Controller{ // or extends CController depending on your code
public function init(){
parent::init(); // no need for this call if you don't have anything in your parent init()
if(array_key_exists('RolePublisher', Yii::app()->authManager->getRoles(Yii::app()->user->id)))
$this->defaultAction='publisher'; // name of your action
else if (array_key_exists('RoleAuthor', Yii::app()->authManager->getRoles(Yii::app()->user->id)))
$this->defaultAction='author'; // name of your action
}
// ... rest of your code
}
?>
Check out CAuthManager's getRoles(), to see that the returned array will have format of 'role'=>CAuthItem object, which is why i'm checking with array_key_exists().
Incase you don't know, the action name will be only the name without the action part, for example if you have public function actionPublisher(){...} then action name should be: publisher.
Another, simpler, thing you can do is keep the default action the same, but that default action simply calls an additional action function depending on what kind of user is logged in. So for example you have the indexAction function conditionally calling this->userAction or this->publisherAction depending on the check for who is logged in.
I think you can save "first user page" in user table. And when a user is authenticated, you can load this page from database. Where you can do this? I think best place is UserIdentity class. After that, you could get this value in SiteController::actionLogin();
You can get or set "first page" value:
if (null === $user->first_page) {
$firstPage = 'site/index';
} else {
$firstPage = $user->first_page;
}
This is a complete class:
class UserIdentity extends CUserIdentity
{
private $_id;
public function authenticate()
{
$user = User::model()->findByAttributes(array('username' => $this->username));
if ($user === null) {
$this->errorCode = self::ERROR_USERNAME_INVALID;
} else if ($user->password !== $user->encrypt($this->password)) {
$this->errorCode = self::ERROR_PASSWORD_INVALID;
} else {
$this->_id = $user->id;
if (null === $user->first_page) {
$firstPage = 'site/index';
} else {
$firstPage = $user->first_page;
}
$this->errorCode = self::ERROR_NONE;
}
return !$this->errorCode;
}
public function getId()
{
return $this->_id;
}
}
/**
* Displays the login page
*/
public function actionLogin()
{
$model = new LoginForm;
// if it is ajax validation request
if (isset($_POST['ajax']) && $_POST['ajax'] === 'login-form') {
echo CActiveForm::validate($model);
Yii::app()->end();
}
// collect user input data
if (isset($_POST['LoginForm'])) {
$model->attributes = $_POST['LoginForm'];
// validate user input and redirect to the previous page if valid
if ($model->validate() && $model->login())
$this->redirect(Yii::app()->user->first_page);
}
// display the login form
$this->render('login', array('model' => $model));
}
Also, you can just write right code only in this file. In SiteController file.