API display user details - laravel

My current API is:
[{"id":43,"title_id":1,"user_id":1,"comment":"asdasddsa","season":null,"episode":null,"created_at":null,"updated_at":"2019-04-30 09:14:15"}]
I want to replace user_id to the user details from the user table according to id. I would like to see it this:
[{"id":43,"title_id":1,"users":{id:"1",username:"test",email:"example#example.com"},"comment":"asdasddsa","season":null,"episode":null,"created_at":null,"updated_at":"2019-04-30 09:14:15"}]
Controller
public function index($id)
{
$test = Title::where('title_id', $id)->get();
return $test;
}
Title model
public function user()
{
return $this->belongsTo('App\User', 'user_id');
}
protected $table = 'title';
if I use foreach() in my controller like this:
$test = Title::where('title_id', $id)->get();
foreach($test as $details)
{
return $details->user->username;
}
It gets the user details, but it isn't in the API. How can I get it in API?

For API , it is better to Eloquent resource
Example in your Title resource it will be like
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class Title extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'title_id' => $this->title_id,
'title_name' => $this->title_name,
'user_name' => $this->user->name,
];
}
public function with($request)
{
return [
'status' => '200',
'response' => 'Title resource',
];
}
}
In your controller
public function show(Title $title)
{
return new TitleResource($title);
}
In 5.4 it will be like
public function show(Title $title)
{
$title->user_name = $title->user->name;
return $title->toJson();
}
and it should work fine
example in tinker
>>> $user = App\User::find(1);
=> App\User {#3161
id: 1,
name: "superadmin",
email: "superadmin#example.com",
created_at: "2018-12-13 12:09:07",
updated_at: "2018-12-22 06:47:13",
}
>>> $user->pk = 's';
=> "s"
>>> $user
=> App\User {#3161
id: 1,
name: "superadmin",
email: "superadmin#example.com",
created_at: "2018-12-13 12:09:07",
updated_at: "2018-12-22 06:47:13",
pk: "s",
}

You can do this:
$test= Title::where('title_id', $id)->get();
And if I understand, there is one user for one title, correct? Then you can do this to retrieve the user of a title:
$test->user = User::find($test->user_id);
And finally return the title in a json response:
return response()->json($test, 200);
Your back will send what you want, something like this:
[{"id":43,"title_id":1,"users":{id:"1",username:"test",email:"example#example.com"},"comment":"asdasddsa","season":null,"episode":null,"created_at":null,"updated_at":"2019-04-30 09:14:15"}]

Related

Get relationship from another relationship with Eloquent and Laravel 8

I try to get Address with Country's name, which belongs to InvoiceData. Invoice Data belongs to Personal or Business Profile. Last condition was made with morphTo() Laravel's feature.
Simplified db structure:
personal_profiles:
id, name
invoice_data:
id, address_id, invoiceable_id, invoiceable_type
addresses:
id, country_id, postal_code, city
countries
id, name
Then models:
class UserProfile extends Model
{
public function invoiceData()
{
return $this->morphOne(InvoiceData::class, 'invoiceable');
}
}
class InvoiceData extends Model
{
public function imageable()
{
return $this->morphTo();
}
public function address()
{
return $this->belongsTo(Address::class)->with(Country::class);
}
}
class Address extends Model
{
public function country()
{
return $this->belongsTo(Country::class);
}
}
class Country extends Model
{
public function address()
{
return $this->hasMany(Address::class);
}
}
And the Controller:
public function getPersonalInvoiceData()
{
/** #var User $user */
$user = \Auth::user();
/** #var UserProfile $profile */
$profile = $user->userProfile;
/** #var InvoiceData $invoiceData */
$invoiceData = $profile->invoiceData;
/** #var Address $address */
$address = $invoiceData->address;
//$address = $invoiceData->address()->with(Country::class)->get();
$responseData = [
'name' => $user->name,
'surname' => $user->surname,
'address' => $address,
];
return response()->json($responseData);
}
There is no problem with get address:
"address": {
"id": 1,
"country_id": 171,
"city": "Foo City",
"postal_code": "00-100",
}
But I don't know how to "replace" country_id with related name using Eloquent. As you can see, I tried to use with() on but I got the exception with message: Call to undefined relationship [App\\Models\\Country] on model [App\\Models\\Address]. I thought that relations I mentioned are enough. For what did I forgot?
You shouldn't use class in "with" there should be the relation name but I think you don't want to use with method because it will get all countries when you call get method.
All you have to do is:
$address = $invoiceData->address->load('country');

Laravel Excel 3.1 passing data from controller to class

I have upgraded the laravel excel library (Maatswebsite) from 2x to 3.1 (running Laravel 5.6/php 7.1) and trying to make my old data work (download exported file) and cannot work out how to pass my $data (which is an array from a foreach DB query (not eloquent) in controller) to the UsersExport.php class...
If I manually create a test collection (mirroring my $data array) in the class:
return collect([
[
'name' => 'F Name 1',
'surname' => 'Last Name 1',
'email' => 'Email 1'
'date_completed' => 'xx/xx/xx'
],
[
'name' => 'F Name 2',
'surname' => 'Last Name 2',
'email' => 'Email 2',
'date_completed' => 'xx/xx/xx'
]
]);
the above works perfect and the file is created and downloads when I run:
return Excel::download(new UsersExport, 'Test.xlsx');
But I want to pass my array ($data) from the controller to the class and not sure HOW I do this... I am trying to get something like this to work:
return Excel::download(new UsersExport($data), 'Test.xlsx');
From reading the specific posts I could find, I believe I need to create a constructor in the Class to accept my $data - but not sure how, and how to return that data if I succeed in my class accepting the data etc... Is the FromCollection the right option?
private $data;
public function __construct($data)
{
$this->data = $data;
}
Appreciate any assistance.... Thanks in advance.
Your approach is right. then use the collection() function to return that data.
private $data;
public function __construct($data)
{
$this->data = $data;
}
public function collection()
{
return $this->data;
}
if you want passing param data to class you use construct.
Example Controller:
<?php
namespace App\Http\Controllers\Reports;
use App\Http\Controllers\Controller;
use Maatwebsite\Excel\Facades\Excel;
use App\Exports\CustomerinvoiceExport;
use App\Model\OrderInvoiceList;
use Illuminate\Http\Request;
class CustomerInvoiceController extends Controller
{
public function index(Request $request)
{
if ($request->has('start_date')) {
$start_date = $request->start_date;
} else {
$date_now = Carbon::now();
$start_date = $date_now->toDateString();
}
if ($request->has('end_date')) {
$end_date = $request->end_date;
} else {
$date_now = Carbon::now();
$end_date = $date_now->toDateString();
}
$customer_invs = OrderInvoiceList::customer_invoice($start_date, $end_date);
return Excel::download(new CustomerinvoiceExport($customer_invs), 'Customer_Invoice_Report.xlsx');
}
}
}
Class Export
<?php
namespace App\Exports;
use Maatwebsite\Excel\Concerns\FromCollection;
class CustomerinvoiceExport implements FromCollection
{
protected $customer_invs;
/**
* Customer Invoice Report
*/
public function __construct($customer_invs)
{
$this->customer_invs = $customer_invs;
}
/**
* #return invoice_list
*/
public function collection(): array
{
$invoice_list = $this->invoice_list;
...........your logic here....
}
}

How to add own data to POST in YII2?

i've question about adding some data outside form and send it with form data. Look! I have 3 fields ActiveForm:
name (text)
email (email)
course (hidden)
Ok, but i need to add one more named "status". I do not want to add hidden fields, just want to add inside controller or model.
How?
Controller:
public function actionFree()
{
$model = new SubscribeForm();
$this->view->title = "ШКОЛА ПИСАТЕЛЬСКОГО МАСТЕРСТВА: Новичок курс";
if ($post = $model->load(Yii::$app->request->post())) {
if ($model->save()) {
Yii::$app->session->setFlash('success', 'Данные приняты');
return $this->refresh();
}
else {
Yii::$app->session->setFlash('error', 'Ошибка');
}
}
else {
// страница отображается первый раз
return $this->render('free-course', ['model' => $model, 'course_id' => 1]);
}
}
Model:
class SubscribeForm extends ActiveRecord
{
public $fio;
public $email;
public $course;
public $status;
public static function tableName()
{
return 'users';
}
public function rules()
{
return [
// username and password are both required
[['fio', 'email'], 'required'],
[['email'], 'unique'],
['email', 'email'],
['email', 'safe']
];
}
}
You could just set the value in your controller, like this:
public function actionFree()
{
$model = new SubscribeForm();
$model->status = 'your-status-value';
// ... the rest of your code
Or you could add a default value in your model. This way you can still overrule the value from the controller or a form field, but will get this value when nothing else is supplied.
public function rules()
{
return [
['status', 'default', 'value' => 'your-default-status-value'],
// .. other rules
];
}

Laravel conditional array using Resource

I've got People table with columns: person, name, text.
column person can have only values like: physician, dentist, nurse, etc...
I want to send it via api using Laravel Resource, so I created basic PeopleResource.php:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\Resource;
class PeopleResource extends Resource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'name' => $this->name,
'text' => $this->text
];
}
}
this is simple and gives me an array of objects with name and text.
Now I want to make more complicated array that will give me something like this:
Please note that I want to get all person types into my array here. This is the problem of making my array structured - so I can access it like result.physician.name, result.nurse.name etc..
result: {
physician: [{name: "name1", text: "text2"}, {name: "name2", text: "text2"}...],
nurse: [{name: "name1", text: "text2"}, {name: "name2", text: "text2"}...],
dentist: [{name: "name1", text: "text2"}, {name: "name2", text: "text2"}...],
otherTypeOfPerson: [], ....
}
How can I do it using Laravel Resource?
edit: my controller
<?php
namespace App\Http\Controllers;
use App\People;
use Illuminate\Http\Request;
use App\Http\Resources\PeopleResource;
class PeoplesController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$peoples= People::orderBy('id','asc')->get();
return PeopleResource::collection($peoples);
}
edit2: I was thinking about this solution, but this seems not working:
public function toArray($request)
{
return [
if($this->person == 'nurse') {
'nurse' => [
'name' => $this->name,
'text' => $this->text
]
}
if($this->person == 'physician') {
'physician' => [
'name' => $this->name,
'text' => $this->text
]
}
];
}
$result = YourModel:groupBy('person')->get();
dd($result);
//define your field name in model
Use groupBy() on collection.
public function index()
{
$peoples = People::select(['name', 'text', 'person'])->orderBy('id','asc')->get();
return $peoples->groupBy('person')->toArray();
}

Yii2 active record model not saving data

I've built a simple form model & view, a simple AR model, and a simple controller. The form model assigns the correct values to the AR instance, but when I call save(), none of those values are saved in the DB. Any ideas?
The form model:
<?php
namespace app\models;
use Yii;
use yii\base\Model;
class PromptForm extends Model
{
public $name;
public $intro;
public $prompt;
public $notes;
public $questions;
public function attributeLabels()
{
return [
'name' => 'Prompt title',
'intro' => 'Intro',
'prompt' => 'Prompt body',
'notes' => 'Closing notes',
'questions' => 'Exploration questions',
];
}
/**
* #return array the validation rules.
*/
public function rules()
{
return [
[['name', 'prompt'], 'required'],
['name', 'filter', 'filter' => 'trim'],
['name', 'string', 'max' => 255],
[['intro', 'prompt', 'notes', 'questions'], 'default'],
];
}
public function post()
{
if ($this->validate()) {
$prompt = new Prompt();
$prompt->name = $this->name;
$prompt->intro = $this->intro;
$prompt->prompt = $this->prompt;
$prompt->notes = $this->notes;
$prompt->questions = $this->questions;
$prompt->author = \Yii::$app->user->getId();
//die(print_r($prompt, TRUE));
$prompt->save();
return $prompt;
}
return null;
}
}
The AR model:
<?php
namespace app\models;
use Yii;
use yii\db\ActiveRecord;
/**
* Prompt is the model behind the prompt item.
*/
class Prompt extends ActiveRecord
{
public $name;
public $intro;
public $prompt;
public $notes;
public $questions;
public $status;
public $author;
public $id;
/**
* #return string the name of the table associated with this ActiveRecord class.
*/
public static function tableName()
{
return 'prompt';
}
/**
* #return array the attribute labels.
*/
public function attributeLabels()
{
return [
'name' => 'Prompt title',
'intro' => 'Intro',
'prompt' => 'Prompt body',
'notes' => 'Closing notes',
'questions' => 'Exploration questions',
'status' => 'Status',
'author' => 'Author ID',
];
}
}
The controller:
<?php
namespace app\controllers;
use Yii;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\filters\VerbFilter;
use app\models\PromptForm;
use app\models\Prompt;
class PromptsController extends Controller
{
public function actionIndex()
{
// Return a list of all prompts:
return $this->render('index');
}
public function actionNew()
{
if (\Yii::$app->user->isGuest) {
return $this->goHome();
}
$model = new PromptForm();
if ($model->load(Yii::$app->request->post())) {
if ($prompt = $model->post()) {
Yii::$app->getSession()->setFlash('success', 'Your prompt was created successfully!');
return $this->goHome();
} else {
Yii::$app->getSession()->setFlash('error', 'Error while submitting your prompt.');
}
}
return $this->render('create', [
'model' => $model,
]);
}
}
Okay, I figured it out. Turns out that if you declare public attributes in your ActiveRecord model, they obscure the automatic attributes that are created by AR. Data gets assigned to your obscuring attributes but doesn't get sent into the database.
The correct AR model should have been simply this:
<?php
namespace app\models;
use Yii;
use yii\db\ActiveRecord;
class Prompt extends ActiveRecord
{
/**
* #return string the name of the table associated with this ActiveRecord class.
*/
public static function tableName()
{
return 'prompt';
}
}
Use
$prompt->save(false);
If that works that means that some validation rule fails.
Try
if ($model->load(Yii::$app->request->post())) {
if ($prompt = $model->post()) {
$model->save()
Yii::$app->getSession()->setFlash('success', 'Your prompt was created successfully!');
return $this->goHome();
} else {
Yii::$app->getSession()->setFlash('error', 'Error while submitting your prompt.');
}
}
In controller, change your if condition as follow :
if ($prompt = $model->post() !== null) {
This will validate that the value which is return is not null.
Your current validation condition is only validating where value is get assigned to variable $prompt or not. And that's why it's always returns true.
I ran across the same problem recently, when I combine the Active Record class with The Model class. Cause I know that AR actually extends Model in Yii2. Why not write less code.So I move the code from the Model to the AR.
$model = new User();
$model->load(Yii::$app->request->post())
But the AR's _attribute didn't get the post data in the form. the form data is actually in a Model object.
object(app\models\User)#39 (12) { ["password"]=> string(6) "google"
["newpass"]=> NULL ["name"]=> string(5) "Jane1" ["email"]=> string(16)
"jane#outlook.com" ["_attributes":"yii\db\BaseActiveRecord":private]=>
array(2) { ["password_hash"]=> string(60)
"$2y$13$.vNKpmosLjW/oYAhIezOZOj8rIG6QJvQj8tGHN2x78.75poXVn6Yi"
["auth_key"]=> string(32) "4XggNakVd-oeU28ny7obdw7gOmZJ-Rbu" }
simply delete the public attribute you want mass assign to the AR instance will make it work.
For who is struggling with this problem, I would remember to check the beforeSave method, if present. I mistakenly commented out the return statement.
public function beforeSave($insert)
{
// never toggle comment on this!!!
return parent::beforeSave( $insert);
}
How to Troubleshoot
First thing you should add while developing to your _form.php is errorSummary():
<?php $form = ActiveForm::begin(); ?>
// Some input fields
...
<?= $form->errorSummary($model); ?> // <--- Add this
...
<?php ActiveForm::end(); ?>
Simplify
Why not use scenarios instead if there is some minimal variation form to form:
In your model:
public function rules()
{
return [
[['field_1'], 'required', 'on' => self::SCENARIO_ADD], // only on add
[['field_2'], 'required', 'on' => self::SCENARIO_UPDATE], // only on update
[['field_3', 'field_4'], 'required'], // required all the time
];
}
In your controller:
public function actionAdd()
{
$model = new Model();
$model->scenario = Model::SCENARIO_ADD;
if ($model->load(Yii::$app->request->post())) {
return $this->redirect(['view', 'id' => $model->id]);
}
return $this->render('add', ['model' => $model]);
}
Behaviors
Alternatively, rather than assign the user directly in your model, you could use a behavior like so:
https://www.yiiframework.com/doc/api/2.0/yii-behaviors-blameablebehavior
/**
* {#inheritdoc}
*/
public function behaviors()
{
return [
[
'class' => \yii\behaviors\BlameableBehavior::className(),
'value' => Yii::$app->user->identity->username,
],
[
'class' => \yii\behaviors\TimestampBehavior::className(),
'value' => new \yii\db\Expression('NOW()'),
],
[
'class' => 'sammaye\audittrail\LoggableBehavior',
'userAttribute' => 'updated_by', //blameable attribute of the current model.
'ignored' => ['updated_by', 'updated_at'], // This ignores fields from a selection of all fields, not needed with allowed
],
];
}

Resources