How to use relationships in laravel 8 - laravel

my question has two parts
Firstly, My if statement is not working. My if statement is as followed:
if ($request->is_published) {
$resources_page->published_at = now();
}
This is stored in my controller, I have a model for this and it is as followed:
public function is_published()
{
return $this->published_at !== null;
}
It is meant to check whether my checkbox is checked and return the timestamp, I have it cast in my model like followed:
protected $casts = [
'published_at' => 'datetime',
];
And in my view:
#include('components.form.input-checkbox', [
'label' => 'Publish?',
'form_object' => 'page',
'name' => 'is_published'
])
Could anyone elude to the answer?
Secondly, when trying to sync, it is not storing to my resources_category_resources_page table
In my controller, i have the following code
$resources_page->resources_categories()->sync(
ResourcesCategory::whereIn('slug', $request->resources_categories)->pluck('id')
);
In my model I have the relationships declared properly, so I don't know why its not storing?

Related

Eloquent eager loading specific columns

I have two models :Product and category
which are linked by a one-to-many relationship. A category has several products. I would like to select specific columns from each model.
Here is the query I have, but I have all the columns with category_id, but I want the category name instead of id. How can I do that. Thank you in advance.
here is the method in controller
$products = Product::with('categories:id,name')->get();
if ($products) {
$response = ['api_status' => 1, 'api_message' => 'success', 'data' => $products];
return response()->json($response);
} else {
$response = ['api_status' => 0, 'api_message' => 'Error'];
return response()->json($response);
}
Here is category model
class Categorie extends Model
{
use HasFactory, SoftDeletes;
protected $fillable =['name','redirect'];
public function products()
{
return $this->hasMany(product::class);
}
}
and the product model is:
class Product extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'name',
'description',
'detail', 'img',
'categorie_id', 'onSale',
'costPrice', 'inStock', 'salePrice'
];
public function categories()
{
return $this->belongsTo(Categorie::class);
}
}
here is the response:
To modify the output of your model I'd suggest using an API resource. This will give you more granular control about how a resource is returned by the API. A resource is also the best point to modify certain values.
use Illuminate\Http\Resources\Json\JsonResource;
class ProductResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'description' => $this->description,
'detail' => $this->detail,
'img' => $this->img,
'category_id' => $this->categorie->name,
'category_name' => $this->categorie->name,
'onSale' => $this->onSale,
'costPrice' => $this->costPrice,
'inStock' => $this->inStock,
'salePrice' => $this->salePrice,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'deleted_at' => $this->deleted_at,
'categories' => $this->categories ?? null,
];
}
}
This way you can manually specify which values your response should have.
In your controller you can include the populated array in your response by manually filling the toArray method with the current request object or just by using the resolve method which basically does the previous task for you:
$response = [
'api_status' => 1,
'api_message' => 'success',
'data' => ProductResource::collection($products)->resolve()
];
You can select particular fields from the relationship but you always need to select any keys involved in the relationship:
$products = Product::with('categories:id,name')->get();
Now each Product has its 'categories' loaded and those Category models only have the id and name fields.
Importantly:
The relationship categories is named incorrectly, it should be categorie in this case as the foreign key on Product is categorie_id and it is a singular relationship, it does not return multiple results.
Product::with('categorie:id,name')->get()
If you want to keep the name categories you would have to define the foreign key used when defining the belongsTorelationship, the second argument.
If you need to transform the structure of any of this that is a different thing and you will be walking into transformers or an API Resource.
Not sure how you want your data to look but this is the structure you will have by eager loading records, so if you need a different structure then what you get you will have to show an example.

Laravel avoid duplicate entry from model

I'm building a Laravel API. I have a models called Reservations. I want to avoid that a user creates two reservations for the same product and time period.
I have the following:
$reservation = Reservation::firstOrCreate([
'listing_id' => $request->listing_id,
'user_id_from' => $request->user_id_from,
'start_date' => $request->start_date,
'end_date' => $request->end_date,
]);
Edit after comments:
I'm also using validation
$validator = Validator::make($request->all(), [
'listing_id' => 'required|exists:listings,id',
'user_id_from' => 'required|exists:users,id',
'start_date' => 'required|date_format:"Y-m-d"|after:today',
'end_date' => 'required|date_format:"Y-m-d"|after:start_date'
]);
if ($validator->fails()) {
return response()->json(['error' => 'Validation failed'], 403);
}
Validation is working properly.
End of Edit
In my model I have casted the start_date and end_date as dates.
class Reservation extends Model
{
protected $fillable = ['listing_id', 'start_date', 'end_date'];
protected $dates = [
'start_date',
'end_date'
];
....
....
Documentation says:
The firstOrCreate method will attempt to locate a database record
using the given column / value pairs
However I notice that I'm still able to insert entries with the same attributes.
Any idea what I'm doing wrong or suggestions to fix it?
Probably there's a better way than this, but you can create an static method on Reservation to do this, like:
public static function createWithRules($data) {
$exists = $this->where('product_id', $data['product_id'])->whereBetween(*date logic that i don't remember right now*)->first();
if(!$exists) {
* insert logic *
} else {
* product with date exists *
}
}
So you can call Reservation::createWithRules($data)
You can achieve this using Laravel's built in ValidateRequest class. The most simple use-case for this validation, is to call it directly in your store() method like this:
public function store(){
$this->validate($request, [
'listing_id' => 'required|unique,
'start_date' => 'required|unique,
//... and so on
], $this->messages);
$reservation = Reservation::firstOrCreate([
'listing_id' => $request->listing_id,
'user_id_from' => $request->user_id_from,
'start_date' => $request->start_date,
'end_date' => $request->end_date,
]);
}
With this, you're validating users $request with by saying that specified columns are required and that they need to be unique, in order for validation to pass.
In your controller, you can also create messages function to display error messages, if the condition isn't met.
private $messages = [
'listing_id.required' => 'Listing_id is required',
'title.unique' => 'Listing_id already exists',
//... and so on
];
You can also achieve this by creating a new custom validation class:
php artisan make:request StoreReservation
The generated class will be placed in the app/Http/Requests directory. Now, you can add a few validation rules to the rules method:
public function rules()
{
return [
'listing_id' => 'required|unique,
'start_date' => 'required|unique,
//... and so on
];
}
All you need to do now is type-hint the request on your controller method. The incoming form request is validated before the controller method is called, meaning you do not need to clutter your controller with any validation logic:
public function store(StoreReservation $request)
{
// The incoming request is valid...
// Retrieve the validated input data...
$validated = $request->validated();
}
If you have any additional question about this, feel free to ask. Source: Laravel official documentation.

Revisionable presenters not working

First I find a ticket.
$tick = App\Tickets::find(14);
Then I find a revision for the same:
$rev = $tick->latestRevision;
But it gives me a error:
App\Presenters\Revisions\Tickets #0000000021ba4aef0000000179e23051 {}
When I see in database, the revisions table is updated with a revision.
And this is my Presenters code:
namespace App\Presenters\Revisions;
use Sofa\Revisionable\Laravel\Presenter;
class Tickets extends Presenter {
protected $passThrough = [
'stage_id' => 'stage.stage_name',
];
protected $actions = [
'created' => 'Created at',
'updated' => 'Updated at',
'deleted' => 'Deleted',
'restored' => 'Restored',
];
}
So this is my relation from Tickets model.
public function stage() {
return $this->hasOne('App\Stages');
}
And I used stage.stage_name in passThrough, but still there is no result.
Also, when I do $revision->old('stage_id'); , I get null
I am using this package: https://github.com/jarektkaczyk/revisionable
That's not an error, that is the tinker output showing you your App\Presenters\Revisions\Tickets object.
Do a $rev->getDiff() and it should work fine.

cant get ESelect2 extension in yii concatenate two or more columns in the dropdown

Eselect2 is the extension am using and myADropDown() fetches the data and displays but then my need is concatenate two or more columns in the dropdown
model1 view
<?php $this->widget('ext.select2.ESelect2', array(
'name' => 'id',
'model' => $model,
'options' => array(
'placeholder' => $model->getAttributeLabel('id'),
),
'data' => $model->myADropDown(),
));?>
model1
public function getConcatened()
{
return $this->name.' '.$this->locate.' '.$this->rating;
}
public function myADropDown()
{
$vid=Yii::app()->SESSION['vid'];
$gid=Model2::model()->xyz($vid);
$list=CHtml::listData($gid,'id','concatened');
return $list;
}
// id is fk in another tbl
in the dropdownlist my need is name ,location,rating for each person , am unable to do so
Please Let me know how do i achieve it
For concatenating the table fields in listdata, Please check this Yii forum answer:
Link
As per the link suggestion, you need to add a function in model to get concatenated string.
public function __get($key){
$action = 'get'.str_replace(' ','',ucwords(str_replace('_',' ',$key)));
if(method_exists($this, $action)) return $this->$action();
return parent::__get($key);
}

CakePHP Retrieving data from habtm

I haven't been able to find a solution that solves this. I am trying to query my database for a list of groups to which a user belongs. Users can be in multiple groups, and groups can have multiple users.
I have "users", "groups", and "groups_users" tables. Each has an "id" field, and the "groups_users" table has two other fields, "group_id" and "user_id".
I think all the tables are properly formed.
My User.php, and Group.php model files look like this
//app/Model/User.php
class User extends AppModel {
public $recursive = 1;
public #hasAndBelongsToMany = array(
'Group' => array(
'className' => 'Group',
'joinTable' => 'groups_users',
'foreignKey' => 'user_id',
'associationForeignKey' => 'group_id'
)
);
}
//app/Model/Group.php
class Group extends AppModel {
public $recursive = 1;
public #hasAndBelongsToMany = array(
'User' => array(
'className' => 'User',
'joinTable' => 'groups_users',
'foreignKey' => 'group_id',
'associationForeignKey' => 'user_id'
)
);
}
Then, in my Controller file I have this
//app/Controller/ProjectController.php
class ProjectController extends AppController {
public $uses = array('Groups', 'Users');
...
$this->set('groupsForUser', $this->Groups->find('all', array(
'conditions' => array('Users.UserName' => 'testuser1')
));
}
Every time I try to display the data, I get the error message
Error: ..... Unknown column 'Users.UserName' in 'where clause'
And it shows me the SQL it's trying to run:
SELECT Groups.id, ... FROM accounts.groups AS Groups WHERE Users.UserName = 'testuser1'
Obviously the association between the tables isn't happening properly and the SQL query it is sending in isn't joined properly, but I can't figure out what's going wrong here. I've tried varieties like
.....$this->Groups->Users->find(....
And stuff like that, but nothing seems to work.
Help!
I think You should use "User.UserName" instead of "Users.UserName" in your ProjectController.php because you have "User" in your associations So model name should be same.
//app/Controller/ProjectController.php
class ProjectController extends AppController {
public $uses = array('Groups', 'User');
...
$this->set('groupsForUser', $this->Groups->find('all', array(
'conditions' => array('User.UserName' => 'testuser1')
));
}
I had to take a completely different approach to the problem. It's mostly "Cake-y".
I simplified the models a bit by taking out the $recursive bit and trimmed down the HABTM part, removing the joinTable, foreignKey, and associationForeignKey bits)
The ProjectController got the code:
public $uses = array('Group', 'User');
$arrayGroupsForUser = array();
....
$results = $this->User->Find('first', array(
'conditions' => array('User.UserName' => 'testuser1')
));
foreach($results['Group'] as $result) {
array_push($arrayGroupsForUser, $result['name']);
}
$this->set('groupsForUser', $arrayGroupsForUser);
(Thank you ned stark for noting the need for a singular "User". I can't upvote your answer with my limited reputation yet.)
The "$this->set(.....);" line is to pass just the array of group names to the View to use.
This link http://rottmann.net/2012/07/cakephp-hasandbelongstomany-relationship-queries-demystified/ was a big help in helping me solve this.

Resources