Hi i am working on a update method for updating a profile, right now i am updating the profile by passing the model as a parameter but i am wanting to pass in the id of a profile so the route is patch('/profiles/podcast/{id}'' i am quite new to laravel so im wondering how do i modify the controller and phpunit test to update this way when grabbing objects in laravel?
Update function in the controller:
public function update(PodcastProfile $podcastProfile)
{
$this->user = Auth::user();
$this->podcastProfile = $podcastProfile;
if (!$this->hasPodcastProfile()) {
abort(400, "You don't have a podcast profile configured");
}
$this->validation();
$this->transaction();
return $this->podcastProfile->toJson();
}
This is the current route for the update method
Route::patch('/profiles/podcast/{podcastProfile}', 'PodcastProfileController#update');
This is the phpunit test case for the function
/**
* #test
*/
public function it_should_update_podcast_profile()
{
$podcastDetails = $this->handlePostRequestToController();
$this->json('patch', '/profiles/podcast/' . $podcastDetails['id'], $this->updateData)
->assertSuccessful();
$this->checkPodcastProfile($this->updateData, $podcastDetails['id']);
$this->checkGuestFormats($this->updateData['guest_format']);
$this->checkAvailability($this->updateData['availability']);
$this->checkEquipment($this->updateData['equipment']);
$this->checkCategories($this->updateData['categories']);
$this->checkLocation($this->updateData['country'], $this->updateData['city']);
}
Hej,
if I understand your question correctly you would just pass the id of that profile instead, just like you said:
Route::patch('/profiles/podcast/{podcastProfileId}','PodcastProfileController#update');
and then fetch the profile by that given id.
so something like:
$this->podcastProfile = App\PodcastProfile::find($podcastProfileId)
Also i feel like choosing the route-model-binding approach like #Remul described would be the better approach.
Related
I have created a project that have three tables (hardwarePlacement , HardwareUnitType, hardwareUnit)
And created the entities / controllers for them, with get, post, put and delete.
And it works perfectly when i test the methods for hardwarePlacement and HardwareUnitType, but the last table "hardwareUnit" is a relational table to the other two. so i have Forign keys (hardwarePlacementId and HardwareUnitTypeId).
So when i from postman try to make a post request, i get the error: "that my setHardwareUnitTypeId and hardwarePlacementId must be of type integer".
In my HardwareUnit entity i have the following for the other tables:
#[ORM\ManyToOne(inversedBy: 'hardwareUnits')]
#[ORM\JoinColumn(nullable: false)]
private ?HardwareUnitType $hardwareUnitTypeId = null;
#[ORM\ManyToOne(inversedBy: 'hardwareUnits')]
#[ORM\JoinColumn(nullable: false)]
private ?HardwarePlacement $hardwarePlacementId = null;
public function getHardwareUnitTypeId(): ?HardwareUnitType
{
return $this->hardwareUnitTypeId;
}
public function setHardwareUnitTypeId(?HardwareUnitType $hardwareUnitTypeId): self
{
$this->hardwareUnitTypeId = $hardwareUnitTypeId;
return $this;
}
public function getHardwarePlacementId(): ?HardwarePlacement
{
return $this->hardwarePlacementId;
}
public function setHardwarePlacementId(?HardwarePlacement $hardwarePlacementId): self
{
$this->hardwarePlacementId = $hardwarePlacementId;
return $this;
}
And my create method in HardwareUnit controller:
#[Route('/hardwareUnit', name: 'hardwareUnit_new', methods: ['POST'])]
public function new(ManagerRegistry $doctrine, Request $request): JsonResponse
{
$entityManager = $doctrine->getManager();
$hardwareUnit = new HardwareUnit();
$hardwareUnit->setHardwareUnitTypeId($request->request->get('hardwareUnitTypeId'));
$hardwareUnit->setHardwarePlacementId($request->request->get('hardwarePlacementId'));
$hardwareUnit->setName($request->request->get('name'));
$hardwareUnit->setCreatedDate(new \DateTime());
$hardwareUnit->setEditedDate(new \DateTime());
$entityManager->persist($hardwareUnit);
$entityManager->flush();
return $this->json('Oprettet ny hardware unit id: ' . $hardwareUnit->getId());
}
I have tried retrieving request as intval:
$hardwareUnit->setHardwareUnitTypeId($request->request->get(intval('hardwareUnitTypeId')));
$hardwareUnit->setHardwarePlacementId($request->request->get(intval('hardwarePlacementId')));
But then i get the error that my post value for setHardwareUnitTypeId and setHardwarePlacementId is null
Any suggestions on how i can convert my request to int?
Here is an image of my postman, if it helps:
You're reading the error wrong. It states that the argument of setHardwareUnitTypeId should be of type HardwareUnitType, but that you're providing a string:
App\Entity\HardwareUnit::setHardwareUnitTypeId(): Argument #1 ($hardwareUnitTypeId) must be of type ?App\Entity\HardwareUnitType, string given ...
Looking at your code, the error is quite clear. In your "new" route, you're calling the functions like this:
$hardwareUnit->setHardwareUnitTypeId($request->request->get('hardwareUnitTypeId'));
$hardwareUnit->setHardwarePlacementId($request->request->get('hardwarePlacementId'));
The $request->request->get() method returns a string (or int|float|bool|null), as it's parsing request parameters. It's not "magically" returning objects of the correct type. You need to take that ID and fetch the correct entity.
So what you need to do is the following:
Get the repository for HardwareUnitType and HardwarePlacement. This can be done through dependency injection of the repository directly into your controller action "new". See the docs for more info. You could also use $entityManager that you already have, like this example from docs, to get the repository.
In your controller, use the repository to fetch the entity based on $request->request->get('hardwareUnitTypeId') and $request->request->get('hardwarePlacementId'). You should be able to use the repository's built in find method (see previous example from docs). You'd get something like this:
$entity = $repository->find((int) $request->request->get('...'));
Use the result from find as argument to your setters. Building on on the example at the previous list item: you'd get something like: $hardwareUnit->setHardwareUnitTypeId($entity)
I hope this will help you figuring out your problem. Let me know if you need more help!
I'm confronted to a little problem, i try to add a method to show only online article, but i want to know how do implement this method.
In my DB i have a row is_online (Int) for 0=> offline, 1=>online, how do implement that for my view.
in my models with
public function isonline(){}
or in my PostController in my request of post find.
And after need to add in my admin panel a check box in Post create to change the status off article (online or offline-draft).
You should use Eloquent scope in your code by creating online scope in your model.
public function scopeOnline($query)
{
return $query->where('is_online', 1);
}
Draft posts
public function scopeDrafts($query)
{
return $query->where('is_online', 0);
}
Then in your code you can simply use it like this.
$onlinePosts = Post::online()->get();
$draftPosts = Post::drafts()->get();
You just need to find all records who have flag is_online = 1.
You can write one method in your PostController like
Public function getOnlineRecords{
$records = YourModel::where('is_online','=',1)->get();
return View::make('your_view_path',['records'=>$records]);
}
In your View file, you need to write:-
{{ Form::checkbox('your_field_name', 'value', true) }}
If you want to default the value as checked, pass true as the third argument.
I have a page which has numerous articles. I want to give the user the ability to 'like' an article and to store that like id into a database for reuse later. Im new to Laravel and Php, so here is what i have.
I have models for Article and the Like. and i have this in the public store ArticleController.
public function store()
{
$article = new Article();
$article->body = 'new article body';
$article->title = 'new article Title';
$article->type = 'fashion';
$article->save();
$request = Request::all();
$likes = new Like();
$likes->user_id = Auth::user()->id;
$likes->article_id = $article->id;
$likes->save();
return redirect('article');
}
I followed the tutorial on laravel fundamentals but i think i missed something. This is working for me here. But now i want to change it so that it only takes the existing article and does not make a new one. When i change it to reflect this:
$article = Article::find($id);
It tells me that the $id is not defined. So how do i make $id point to the article the user wants to 'like'?
The question is So how do i make $id point to the article the user wants to 'like'?
This is quite a big/broad question but I'll try to sumarize. At first you need a route for that, for example:
Route::get('article/like/{id}', 'ArticleController#like');
Then in your ArticleController declare the like method, for example:
public function like($id)
{
// Now you can use $id
}
To clarify you, {id} in the route will take the id of the article from the URI so you may use a URI like this:
http://example.com/article/like/10 // Here 10 is the article id
So this was the idea, now implement it and modify the URI or what ever you need to make it fit in your project but remember that, if you want to pass an id to your URI then you have to use a route parameter (i.e: {id} in this case) when declaring the route and your method should recieve that parameter using an argument in the method header, i.e: public function like($id).
Is there any way to update a record in Laravel using eloquent models just if a change has been made to that record? I don't want any user requesting the database for no good reason over and over, just hitting the button to save changes. I have a javascript function that enables and disables the save button according with whether something has changed in the page, but I would like to know if it's possible to make sure to do this kind of feature on the server side too. I know I can accomplish it by myself (meaning: without appealing to an internal functionality of the framework) just by checking if the record has change, but before doing it that way, I would like to know if Laravel eloquent model already takes care of that, so I don't need to re-invent the wheel.
This is the way I use to update a record:
$product = Product::find($data["id"]);
$product->title = $data["title"];
$product->description = $data["description"];
$product->price = $data["price"];
//etc (string values were previously sanitized for xss attacks)
$product->save();
You're already doing it!
save() will check if something in the model has changed. If it hasn't it won't run a db query.
Here's the relevant part of code in Illuminate\Database\Eloquent\Model#performUpdate:
protected function performUpdate(Builder $query, array $options = [])
{
$dirty = $this->getDirty();
if (count($dirty) > 0)
{
// runs update query
}
return true;
}
The getDirty() method simply compares the current attributes with a copy saved in original when the model is created. This is done in the syncOriginal() method:
public function __construct(array $attributes = array())
{
$this->bootIfNotBooted();
$this->syncOriginal();
$this->fill($attributes);
}
public function syncOriginal()
{
$this->original = $this->attributes;
return $this;
}
If you want to check if the model is dirty just call isDirty():
if($product->isDirty()){
// changes have been made
}
Or if you want to check a certain attribute:
if($product->isDirty('price')){
// price has changed
}
You can use $product->getChanges() on Eloquent model even after persisting. Check docs here
I like to add this method, if you are using an edit form, you can use this code to save the changes in your update(Request $request, $id) function:
$post = Post::find($id);
$post->fill($request->input())->save();
keep in mind that you have to name your inputs with the same column name. The fill() function will do all the work for you :)
use only this:
Product::where('id', $id)->update($request->except(['_token', '_method']));
At times you need to compare the newly changed value with the previous one and if you are looking for that here is the solution.
if (
$obj->isDirty('some_field_name') &&
$obj->some_field_name != $obj->getOriginal('some_field_name')
) {
// Make required changes...
}
});
}
The reference of the derived solution is here.
Maybe Laravel has updated since, but wasChanged is working for me better than isDirty in all of these previous answers.
For example:
if($post->wasChanged('status') && $post->status == 'Ready') // Do thing
i want create register page in 3 step:
Step1: Basic Infomation (ext: example.site/register/step1)
Step2: Profile Infomation (ext: example.site/register/step2)
Step3: Done (ext: example.site/register/step3)
looke like:
Somebody can help me?
To Achieve something like this you have two options:
Do it without reloading the page using javascript to show and hide relevant fields. As you declare a different url for each of your gui-prototypes, I think this is not what you want.
You send the data from one page to the other using models and the POST-data. I will explain this way in detail now, as this seems to be your desired solution. Actually this can be done in a single action if you implement functions to check if there is enough data to render the next page (see the model below).
The following code is not tested, but should give you an idea of how it could be done. Lets start with the model.
The model contains the data of all pages. Working with only model across the two input-pages makes things way easier.
class Registration extends CFormModel {
public $firstName;
public $lastName;
//...
public $gender;
public $age;
//...
public function rules()
{
//add all your field-rules here...
}
//further functions like attributeLabels(), etc. as needed
/**
* Returns the fullname as needed on page two
*/
public function getFirstname()
{
return $this->firstName . ' ' . $this->lastName;
}
public function hasValidStepOneData()
{
//validate the fields you need to continue to step two and return a boolean value.
//yii provides this functionality via the validate-function having the abiliy to only
//only validate certain fields at once. If one of them fails return false, otherwise true.
//see here: http://www.yiiframework.com/doc/api/1.1/CModel#validate-detail
}
public function hasValidStepTwoData()
{
//same as above but with values needed for completing step 2
//return boolean
}
}
Now to the controller. According to your URLs fromabove it should be like this:
class RegistrationController extends CController {
public function actionRegSteps()
{
$model = new Registration();
//get the values if there are any
if (isset($_POST['Registration']) {
$model->attributes = $_POST['Registration'];
}
//decide what to do depending on the data available
if ($model->hasValidStepOneData() && $model->hasValidStepTwoData()) {
//all data needed is present...save the data and redirect to success-page
//TODO: save data here...
$model->unsetAttributes();
$this->render('success');
} else if ($model->hasValidStepOneData()) {
//step one is complete...show step2
$this->render('reg_step2', array('model'=>$model));
} else {
//model is still empty...show step 1
$this->render('reg_step1', array('model'=>$model));
}
}
}
The views are quite simple. You have to keep in mind though that you have to add hidden fields on page two to keep the data of step 1. step one only contains input-fields for firstname, lastname and email. Step 2 contains input-fields for password, gender and age and also hidden fields for firstname, lastname and email. The success-view contains no input-fields at all, as the registration-process is completed by then.
Other than this custom-solution, there is a wizard-extension which could come in handy for you. You can find it here: http://www.yiiframework.com/extension/wizard-behavior/
Moreover a similiar solution was already answered here: https://stackoverflow.com/a/3551704/3402681
I provided you the answer above to give you a general insight on how you can handle this. There is no "correct" solution for this. You have to find the one which suits you the best. Hope I could help!