In my model, let's call it "questions", I have "active" field (TINYINT). The idea is, that only one entry in that particular model can be active. So when I update one entry by checking "active" checkbox, this entry should turn into active, and the other one should turn into unactive (I mean that one which has been active before). Any idea what query should I use?
some kind of toggle behavior for example.
this behavior you write makes the model $actsAs = array('Toggable', array('field'=>'active'));
and in your afterSave hook you make an updateAll call to reset all active ones to inactive (except the current id of course)
that would be the cleanest approach.
of course, you can always make a quick-and-dirty one using the models hooks itself for this.
I don't know how to do that. It's little above my current knowledge :)
This is how I figured it out so far:
function admin_edit($id = null) {
$this->Question->id = $id;
if (empty($this->data)) {
$this->data = $this->Question->read();
} else {
if ($this->Question->save($this->data)) {
$active = $this->data['Question']['active'];
$total = $this->Question->find('count');
$i=1;
if ($i == '1') {
while($active <= $total){
$this->Question->query("UPDATE questions SET active = '0'");
$i++;
}
$this->Question->query("UPDATE questions SET active = '1' WHERE id = '$id'");
}
$this->redirect(array('action' => 'index'));
}
}
And it's working but I guess it's not the "cleanest approach". Any tips how to simplify it thereafter?
Related
im quite new in laravel framework, and im from codeigniter.
I would like to add new key and value from database
static function m_get_promotion_banner(){
$query = DB::table("promotion_banner")
->select('promotion_banner_id','promotion_link','about_promotion')
->where('promotion_active','1')
->get();
if($query != null){
foreach ($query as $key => $row){
$query[$key]['promotion_image'] = URL::to('home/image/banner/'.$row['promotion_banner_id']);
}
}
return $query;
}
that code was just changed from codeigniter to laravel, since in codeigniter there are no problem in passing a new key and value in foreach statement
but when i tried it in laravel i got this following error :
Indirect modification of overloaded element of Illuminate\Support\Collection has no effect
at HandleExceptions->handleError(8, 'Indirect modification of overloaded element of Illuminate\Support\Collection has no effect', 'C:\xampp\htdocs\laravel-site\application\app\models\main\Main_home_m.php', 653, array('query' => object(Collection), 'row' => array('promotion_banner_id' => 1, 'promotion_link' => 'http://localhost/deal/home/voucher', 'about_promotion' => ''), 'key' => 0))
please guide me how to fix this
thank you (:
The result of a Laravel query will always be a Collection. To add a property to all the objects in this collection, you can use the map function.
$query = $query->map(function ($object) {
// Add the new property
$object->promotion_image = URL::to('home/image/banner/' . $object->promotion_banner_id);
// Return the new object
return $object;
});
Also, you can get and set the properties using actual object properties and not array keys. This makes the code much more readable in my opinion.
For others who needs a solution you can use jsonserialize method to modify the collection.
Such as:
$data = $data->jsonserialize();
//do your changes here now.
The problem is the get is returning a collection of stdObject
Instead of adding the new field to the result of your query, modify the model of what you are returning.
So, assuming you have a PromotionBanner.php model file in your app directory, edit it and then add these 2 blocks of code:
protected $appends = array('promotionImage');
here you just added the custom field. Now you tell the model how to fill it:
public function getPromotionImageAttribute() {
return (url('home/image/banner/'.$this->promotion_banner_id));
}
Now, you get your banners through your model:
static function m_get_promotion_banner(){
return \App\PromotionBanner::where('promotion_active','1')->get();
}
Now you can access your promotionImage propierty in your result
P.D:
In the case you are NOT using a model... Well, just create the file app\PromotionImage.php:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class PromotionImage extends Model
{
protected $appends = array('imageAttribute');
protected $table = 'promotion_banner';
public function getPromotionImageAttribute() {
return (url('home/image/banner/'.$this->promotion_banner_id));
}
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'promotion_banner_id','promotion_link','about_promotion','promotion_active'
];
just improving, in case you need to pass data inside the query
$url = 'home/image/banner/';
$query = $query->map(function ($object) use ($url) {
// Add the new property
$object->promotion_image = URL::to( $url . $object->promotion_banner_id);
// Return the new object
return $object;
});
I've been struggling with this all evening, and I'm still not sure what my problem is.
I've used ->get() to actually execute the query, and I've tried by ->toArray() and ->jsonserialize() on the data and it didn't fix the problem.
In the end, the work-around I found was this:
$task = Tasks::where("user_id", $userId)->first()->toArray();
$task = json_decode(json_encode($task), true);
$task["foo"] = "bar";
Using json_encode and then json_decode on it again freed it up from whatever was keeping me from editing it.
That's a hacky work-around at best, but if anyone else just needs to push past this problem and get on with their work, this might solve the problem for you.
I have a problem, right now Im using this foreach loop on CakePhp on which I want to add all the values which are still not on the table for the respecting user. To give a little more context, the user has a menu. And the admin can select which one to add for the user to use. On the next code I receive a array with the menus which will be added as so:
//This is what comes on the ['UserMenuAccessibility'] array:
Array ( [menu_accessibility_id2] => 2 [menu_accessibility_id3] => 3 [menu_accessibility_id4] => 4 [menu_accessibility_id5] => 5 [menu_accessibility_id8] => 8 )
I get the ids of the menus which want to be added to the table for the user to use. And I use the next code to add the menus to the table if they are not there still:
//I check if the array has something cause it can come with no ids.
if (!(isset($this->request->data['UserMenuAccessibility']))) {
$this->request->data['UserMenuAccessibility'] = array();
}
$UserMenuAccessibility = $this->request->data['UserMenuAccessibility'];
foreach ($UserMenuAccessibility as $key => $value) {
$conditions = array(
'UserMenuAccessibility.menu_accessibility_id' => $value,
'UserMenuAccessibility.users_id' => $id
);
if ($this->User->UserMenuAccessibility->hasAny($conditions)) {
} else {
$valuemenu['UserMenuAccessibility']['users_id'] = $id;
$valuemenu['UserMenuAccessibility']['menu_accessibility_id'] = $value;
if ($this->User->UserMenuAccessibility->save($valuemenu)) {
} else {
$this->Session->setFlash(__('The user could not be saved. Please, try again.'));
}
}
}
For some reason the array is only saving the last new id which is not on the table and not the rest. For example if I have menu 1 and 2 and add 3 and 4 only 4 gets added to the table. For some reason I cant add all the missing menu ids to the table. Any ideas why this is happening?
Thanks for the help on advance.
It looks like your code will save each item, but each call to save() is overwriting the last entry added as $this->User->UserMenuAccessibility->id is set after the first save and will be used for subsequent saves. Try calling $this->User->UserMenuAccessibility->create() before each save to ensure that the model data is reset and ready to accept new data:-
$valuemenu['UserMenuAccessibility']['users_id'] = $id;
$valuemenu['UserMenuAccessibility']['menu_accessibility_id'] = $value;
$this->User->UserMenuAccessibility->create();
if ($this->User->UserMenuAccessibility->save($valuemenu)) {
}
In cakephp 2.0 $this->Model->create() create work fine. But if you are using cakephp version 3 or greater then 3. Then follow the below code
$saveData['itemId'] = 1;
$saveData['qty'] = 2;
$saveData['type'] = '0';
$saveData['status'] = 'active';
$saveData = $this->Model->newEntity($saveData);
$this->Model->save($materialmismatch);
In normal case we use patchEntity
$this->Model->patchEntity($saveData, $this->request->data);
It will only save last values of array so you have to use newEntity() with data
In cakephp3, patchEntity() is normally used. However, when using it for inserting-new/updating entries in a foreach loop, I too saw that it only saves the last element of the array.
What worked for me was using patchEntities(), which as explained in the patchEntity() doc, is used for patching multiple entities at once.
So simplifying and going by the original code sample to handle multiple entities, it could be:
$userMenuAccessibilityObject = TableRegistry::get('UserMenuAccessibility');
foreach ($UserMenuAccessibility as $key => $value) {
$userMenuAccessibility = $userMenuAccessibilityObject->get($value);//get original individual entity if exists
$userMenuAccessibilities[] = $userMenuAccessibility;
$dataToPatch = [
'menu_accessibility_id' => $value,
'users_id' => $id
]//store corresponding entity data in array for patching after foreach
$userMenuAccessibilitiesData[] = $dataToPatch;
}
$userMenuAccessibilities = $userMenuAccessibilityObject->patchEntities($userMenuAccessibilities, $userMenuAccessibilities);
if ($userMenuAccessibilityObject->saveMany($requisitions)) {
} else {
$this->Session->setFlash(__('The users could not be saved. Please, try again.'));
}
Note: I haven't made it handle if entity doesn't exist, create a new one and resume. That can be done with a simple if condition.
I have a product controller and when I'm saving a new product I want to save some records to another related controller to make a record of what categories the product is associated with.
My code I'm using is:
$this->Product->create();
if ($this->Product->save($this->request->data)) {
$newProductId = $this->Product->getInsertID();
//associate products with categories
foreach($categoriesToSave as $key=>$value) {
$saveArray['CategoriesProduct'] = array('category_id'=>$value, 'product_id'=>$newProductId);
$this->Product->CategoriesProduct->create();
$this->Product->CategoriesProduct->save($saveArray);
$this->Product->CategoriesProduct->clear();
}
}
For some reason though, even if $categoriesToSave has 10 items in it, only the very last one is being saved. So it's obviuosly creating only the one new CategoriesProduct item and saving each record over the top of the last instead of create()-ing a new one.
Can anyone explain what I'm doing wrong and how I can make this work?
The way I would do it would be like this:
//Add a counter
$c = 0
foreach($categoriesToSave as $key=>$value) {
$saveArray[$c]['CategoriesProduct']['category_id'] = $value;
$saveArray[$c]['CategoriesProduct']['product_id'] = $newProductId;
$c++;
}
$this->Product->CategoriesProduct->saveMany($saveArray);
I don't think that is the only way to do it but that should work just fine.
Good luck!
I have a small problem. I am making a site that has Tags and Questions. I have a Question model, Tag model, QuestionsTag model, everything fits together nicely. The user upon asking something puts the tags in the field seperated by a space (foo bar baz) much like on stackoverflow.com.
Now, here is the code to check if a tag already exists or not and entering the tag into the database and the required associations:
function create () {
if (!empty($this->data)) {
$this->data['Question']['user_id'] = 1;
$question = $this->Question->save ($this->data);
/**
* Preverimo če se je vprašanje shranilo, če se je,
* vprašanje označimo.
*/
if ($question) {
$tags = explode (' ', $this->data['Question']['tags']);
foreach ($tags as $tag){
if (($tagId = $this->Tag->existsByName($tag)) != false) {
/**
* Značka že obstaja, torej samo povezemo trenuten
* id z vprašanjem
*/
$this->QuestionsTag->save (array(
'question_id' => $this->Question->id,
'tag_id' => $tagId
));
}
else {
/**
* Značka še ne obstaja, jo ustvarimo!
*/
$this->Tag->save (array(
'name' => $tag
));
// Sedaj pa shranimo
$this->QuestionsTag->save(array(
'question_id' => $this->Question->id,
'tag_id' => $this->Tag->id
));
$this->Tag->id = false;
}
; }
}
}
}
The problem is this, a Question has an id of 1 and I want it to have the tags with id of 1, 2, 3.
When the 2nd and 3rd save get called, Cake sees that in the questions_tags table is already a question with id 1, so it just updates the tag.
But this is not correct, as there should be many questions in that table with the same id, as they refer to different tags belonging to them.
So, is there a way to prevent this? Prevent the save method from UPDATEing?
Thank you!
This behavior isn't specific to HABTM relationships. You are calling the save() method inside of a loop. After the first save, an id value is set and each subsequent save call sees the id and assumes it's an update. Within a loop, you first need to call model->create() to reset an id value that may exist.
From the CakePHP Docs at http://book.cakephp.org/view/75/Saving-Your-Data:
When calling save in a loop, don't forget to call create().
In your case, it would look like this:
$this->QuestionsTag->create();
$this->QuestionsTag->save (array(
'question_id' => $this->Question->id,
'tag_id' => $tagId
));
Check out saveAll. You can make a single call to $this->Question->saveAll(), and it will save any associated data you supply as well. Note that with HABTM data, it will perform a DELETE for any questions_tags associated with that question_id, then perform an INSERT for all the tag_id's included with your data.
if you want to make sure, that a new entry (INSERT) is made rather then an update, you can set $this->create(); right in front of the save call. See http://book.cakephp.org/view/75/Saving-Your-Data (in the upper part of the page): When calling save in a loop, don't forget to call create().
I have a CakePHP model - User - that has ties to an external corporate system. I store some data on those systems and other data locally. In my User::beforeSave() method, I'm trying to set an ID, send the data (with that custom ID) to my corporate systems and then, if it inserts successfully there, return true so that Cake will insert the new user record with that same ID so that I can link them later.
I can't find a way to make this happen. Is there a way to insert a CakePHP record with a user-specified primary key value? I'm using UUIDs so there's (effectively) no opportunity for overlap.
$this->data['User']['id'] = String::uuid()
try {
$user_proxy = new CoreServicesUserProxy();
$corp_user = $user_proxy->CreateUser (
array (
'user' => array (
'UserName' => 'myusername',
'EmailAddress' => $this->data['User']['email'],
'SecurityId' => $this->data['User']['id']
)
)
);
}
catch ( Exception $e ) {
// error handling stuff
return false;
}
I realise you have already been given some hints, but here is some code which might help.
Why not add an external_user_id field to your users table?
<?php
class User extends AppModel {
function beforeSave() {
$ds = ConnectionManager::getDataSource('core_services');
$externalUser = $ds->createUser($this->data);
if (!$externalUser) {
return false;
}
$this->data['User']['external_id'] = $externalUser['id'];
return true;
}
function afterFind($results, $primary) {
// handle different types of find here ('all' vs 'first' vs through relation)
foreach ($results as &$result) {
$result = $this->_mergeExternalUser($result);
}
}
function _mergeExternalUser($user) {
$ds = ConnectionManager::getDataSource('core_services');
$externalUser = $ds->retrieveUser($result['external_id']);
return am($externalUser, $user);
}
}
?>
There is a way - but typically you would add another column to the Users table instead and let CakePHP do it's thing with the primary key. See this Bakery article to know how it's done. Since it is more than a year later, this is for reference mostly. As far as I understand it, this should function well with CakePHP 1.2 as well.