Eloquent relations - attach (but don't save) to Has Many - laravel

I have the following relations set up:
class Page {
public function comments() {
return $this->hasMany('Comment');
}
}
class Comment {
public function page() {
return $this->belongsTo('Page');
}
}
Pretty bog standard. One page can have many comments, and one comment belongs to a single page.
I'd like to be able to create a new page:
$page = new Page;
and a comment
$comment = new Comment;
and attach the comment to the page, without saving any of it
$page->comments->associate($comment);
I've tried the following:
// These are for one-to-many from the MANY side (eg. $comment->page->associate...)
$page->comments->associate($comment); // Call to undefined method Illuminate\Database\Eloquent\Collection::associate()
$page->comments()->associate($comment); // Call to undefined method Illuminate\Database\Query\Builder::associate()
// These 2 are for many-to-many relations, so don't work
$page->comments->attach($comment); // Call to undefined method Illuminate\Database\Eloquent\Collection::attach()
$page->comments()->attach($comment); // Call to undefined method Illuminate\Database\Query\Builder::attach()
// These 2 will (if successful) save to the DB, which I don't want
$page->comments->save($comment); // Call to undefined method Illuminate\Database\Eloquent\Collection::save()
$page->comments()->save($comment); // Integrity constraint violation: 1048 Column 'page_id' cannot be null
The really odd thing is that doing the opposite (attaching the page to the comment) works correctly:
$comment->page()->associate($page);
The relevant docs are here but they don't make any mention of attaching to the ONE side of a one-to-many. Is it even possible? (I feel like it should be)

It sounds like you just want to add the new comment object to the page's comments collection - you can do that easily, using the basic colection add method:
$page = new Page;
$comment = new Comment;
$page->comments->add($comment);

You can't do since there are no ids to link.
So first you need to save the parent ($page) then save the child model:
// $page is existing model, $comment don't need to be
$page->comments()->save($comment); // saves the comment
or the other way around, this time without saving:
// again $page exists, $comment don't need to
$comment->page()->associate($page); // doesn't save the comment yet
$comment->save();

according to Benubird I just wanted to add something as I stumbled over this today:
You can call the add method on a collection like Benubird stated.
To consider the concerns of edpaaz (additional fired query) I did this:
$collection = $page->comments()->getEager(); // Will return the eager collection
$collection->add($comment) // Add comment to collection
As far as I can see this will prevent the additional query as we only use the relation-object.
In my case, one of the entities were persistent while the first (in your case page) was not (and to be created). As I had to process a few things and wanted to handle this in a object manner, I wanted to add a persistent entity object to a non persistent. Should work with both non persistent, too though.
Thank you Benubird for pointing me to the right direction. Hope my addition helps someone as it did for me.
Please have in mind that this is my first stackoverflow post, so please leave your feedback with a bit concern.

Related

The Laravel $model->save() response?

If you are thinking this question is a beginner's question, maybe you are right. But really I was confused.
In my code, I want to know if saving a model is successful or not.
$model = Model::find(1);
$model->attr = $someVale;
$saveStatus = $model->save()
So, I think $saveStatus must show me if the saving is successful or not, But, now, the model is saved in the database while the $saveStatus value is NULL.
I am using Laravel 7;
save() will return a boolean, saved or not saved. So you can either do:
$model = new Model();
$model->attr = $value;
$saved = $model->save();
if(!$saved){
//Do something
}
Or directly save in the if:
if(!$model->save()){
//Do something
}
Please read those documentation from Laravel api section.
https://laravel.com/api/5.8/Illuminate/Database/Eloquent/Model.html#method_getChanges
From here you can get many option to know current object was modified or not.
Also you can check this,
Laravel Eloquent update just if changes have been made
For Create object,
those option can helpful,
You can check the public attribute $exists on your model
if ($model->exists) {
// Model exists in the database
}
You can check for the models id (since that's only available after the record is saved and the newly created id is returned)
if(!$model->id){
App::abort(500, 'Some Error');
}

How to acces is_* property in laravel model?

I am working with laravel 4.2 and have table in db with property is_active.
When I try to access this model property:
$model->is_active
I am getting following error:
Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation
So question is how to access this property?
Please do not recommend to rename this field in the database if possible because this is already existing database in production.
Here is my model class:
class Position extends \Eloquent {
protected $table = "hr_positions";
protected $fillable = ['slug', 'info_small', 'info_full', 'is_active', 'start_date', 'end_date', 'tags', 'user_create_id', 'user_update_id'];
use \MyApp\Core\StartEndDateTrait;
public function postulations(){
return $this->hasMany('Postulation', 'position_id', 'id');
}
}
Latest notice:
All this error ocurrs on a page where I am creating my entity. In the controller before forwarding to the page I am doing:
$position = new \Position();
and then, for example, following code produce error as well:
dd(($position->getAttribute('is_active')));
but if I replace $position = new \Position(); with
$position = \Position::first();
error is gone?
What is going on here?????
Laravel does a lot of magic behind the scenes, as in, calls a lot of php magic methods.
If a called property is not defined, __call is invoked which in Eloquent calls getAttribute().
Steps taken by getAttribute($key) are
Is there a database field by this key? If so, return it.
Is there a loaded relationship by this key? If so, return it.
Is there a camelCase method of this key? If so, return it. (is_active looks for isActive method)
Returns null.
The only time that exception is thrown is in step 3.
When you create a new instance, eloquent has no idea what kind of fields it has, so if you have a method by the same name, it will always throw a relation error, this seems to be the case in both Laravel4 and Laravel5.
How to avoid it? Use the getAttributeValue($key) method. It has no relation checks and returns null by default.
Alternatively you can also add a get mutator for your field.
I have found a hack for this. Still not ideal but at least I have some solution. Better any than none.
So This code produce problem:
$position = new \Position();
if($position->is_active){
//
}
and this one works fine, this is solution even hacky but solution:
$position = new \Position(['is_active' => 0]);
if($position->is_active){
//
}
I will wait if someone give better, cleaner solution. If no one comes in next few days I will accept mine.

How to override save() method in a component

Where and how I am overriding the save method in Joomla 3.0 custom component ?
Current situation:
Custom administrator component.
I have a list view that displays all people stored in table.
Clicking on one entry I get to the detailed view where a form is loaded and it's fields can be edited.
On save, the values are stored in the database. This all works fine.However, ....
When hitting save I wish to modify a field before storing it into the database. How do I override the save function and where? I have been searching this forum and googled quiet a bit to find ways to implement this. Anyone who give me a simple example or point me into the right direction ?
Thanks.
Just adding this for anyone who wants to know the answer to the question itself - this works if you explicitly wish to override the save function. However, look at the actual solution of how to manipulate values!
You override it in the controller, like this:
/**
* save a record (and redirect to main page)
* #return void
*/
function save()
{
$model = $this->getModel('hello');
if ($model->store()) {
$msg = JText::_( 'Greeting Saved!' );
} else {
$msg = JText::_( 'Error Saving Greeting' );
}
// Check the table in so it can be edited.... we are done with it anyway
$link = 'index.php?option=com_hello';
$this->setRedirect($link, $msg);
}
More details here: Joomla Docs - Adding Backend Actions
The prepareTable in the model (as mentioned above) is intended for that (prepare and sanitise the table prior to saving). In case you want to us the ID, though, you should consider using the postSaveHook in the controller:
protected function postSaveHook($model, $validData) {
$item = $model->getItem();
$itemid = $item->get('id');
}
The postSaveHook is called after save is done, thus allowing for newly inserted ID's to be used.
You can use the prepareTable function in the model file (administrator/components/yourComponent/models/yourComponent.php)
protected function prepareTable($table)
{
$table->fieldname = newvalue;
}

CodeIgniter - Dynamic URL segments

I was wondering if someone could help me out.
Im building a forum into my codeigniter application and im having a little trouble figuring out how i build the segments.
As per the CI userguide the uri is built as follows
www.application.com/CLASS/METHOD/ARGUMENTS
This is fine except i need to structure that part a bit different.
In my forum i have categories and posts, so to view a category the following url is used
www.application.com/forums
This is fine as its the class name, but i want to have the next segment dynamic, for instance if i have a category called 'mycategory' and a post by the name of 'this-is-my-first-post', then the structure SHOULD be
www.application.com/forums/mycategory/this-is-my-first-post
I cant seem to achieve that because as per the documentation the 'mycategory' needs to be a method, even if i was to do something like /forums/category/mycategory/this-is-my-first-post it still gets confusing.
If anyone has ever done something like this before, could they shed a little light on it for me please, im quite stuck on this.
Cheers,
Nothing is confusing in the document but you are a little bit confused. Let me give you some suggestions.
You create a view where you create hyperlinks to be clicked and in the hyperlink you provide this instruction
First Post
In the controller you can easily get this
$category = $this->uri->segment(3);
$post = $this->uri->segment(4);
And now you can proceed.
If you think your requirements are something else you can use a hack i have created a method for this which dynamically assign segments.
Go to system/core/uri.php and add this method
function assing_segment($n,$num)
{
$this->segments[$n] = $num;
return $this->segments[$n];
}
How to use
$this->uri->assign_segment(3,'mycategory');
$this->uri->assign_segment(4,'this-is-my-first-post');
And if you have error 'The uri you submitted has disallowed characters' then go to application/config/config.php and add - to this
$config['permitted_uri_chars'] = 'a-z 0-9~%.:_\-';
You could make a route that forwards to a lookup function.
For example in your routes.php add a line something like;
$route['product/(:any)/(:any)'] = "forums/cat_lookup/$1/$2";
This function would then do a database lookup to find the category.
...
public function cat_lookup($cat, $post) {
$catid = $this->forum_model->get_by_name($cat);
if ($catid == FALSE) {
redirect('/home');
}
$post_id = $this->post_model->get_by_name($post);
/* whatever else you want */
// then call the function you want or load the view
$this->load->view('show_post');
}
...
This method will keep the url looking as you want and handle any problems if the category does not exist.Don't forget you can store the category/posts in your database using underscores and use the uri_title() function to make them pretty,
Set in within config/routes.php
$route['song-album/(:any)/:num'] = 'Home/song_album/$id';
fetch in function with help of uri segment.
$this->uri->segment(1);

Magento returning incorrect customer data on frontend pages

isn't this the right method to get Name of logged in customer?
<?php echo Mage::helper('customer')->getCustomer()->getName(); ?>
I have a website with live chat functionality. Yesterday I have been asked to pass email address and the name of the logged into the user into the Javascript Tracking variable code placed in the head section of the website. So that the operators could see who is on the website and whom are they talking to without any need to ask about their information.
So I passed the information from Magento into the Javascript code but now I see this very strange thing happening. For example,
If I am logged in with credentials Name = John Email =
john12#yahoo.com
Then This name and email variable values are changing with the change of pages. For example if I click on any product page the variable values which I am passing changes to some other user's information.
Name becomes Ricky Email becomes ricky23#gmail.com
this variable values are kept on changing back to john and from john to something else with the change of pages. So operator does not have any idea whom are they talking because the values are kept on changing. Also, user ricky or who ever it changes to also exist in the database. so it is picking up random person from the database.
This is what i did to pass the code to javascript. Please let me know if that is not the right code to pass the information. Please check the php code I am using to fetch information from Magento. Roughly, I receive incorrect value once in 5 times. Please provide some assistance. Thanks in advance.
<?php
$customer = Mage::getSingleton('customer/session')->getCustomer();
$email = $customer->getEmail();
$firstname = $customer->getFirstname();
$lastname= $customer->getLastname();
$name = $firstname . ' ' . $lastname;
?>
<script type="text/javascript">
if (typeof(lpMTagConfig) == "undefined"){ lpMTagConfig = {};}
if (typeof(lpMTagConfig.visitorVar) == "undefined"){ lpMTagConfig.visitorVar = [];}
lpMTagConfig.visitorVar[lpMTagConfig.visitorVar.length] = 'Email=<?php echo $email; ?>';
lpMTagConfig.visitorVar[lpMTagConfig.visitorVar.length] = 'Name=<?php echo $name; ?>';
</script>
I'm also attaching a snap shot
I'd be interested to hear how you're adding this code to the page? Is it in it's own block, or are you adding it to footer.phtml, or similar? If your adding to an existing block be sure to check the block caching settings of that template.
To confirm the caching hypothesis I'd ask the following:
Do you get the same name, all the time, on the same page? When you refresh the page, do you get the same name and email in the Javascript?
Does the problem persist with caching disabled?
This doesn't sound like a singleton problem at all. Each execution of the PHP script is isolated from the others, serving one page request. There's no chance of another customer's object moving between invokations of the script.
It is a matter of understanding the singleton pattern. If you call your code twice:
$customer_1 = Mage::helper('customer')->getCustomer()->getName();
$customer_2 = Mage::helper('customer')->getCustomer()->getName();
you get two different instances of the object. But... if one of them has already implemented a singleton pattern in its constructor or has implemented a singleton getInstance then both objects will actually point to the same thing.
Looking at the customer/helper/Data.php code you can see the function
public function getCustomer()
{
if (empty($this->_customer)) {
$this->_customer = Mage::getSingleton('customer/session')->getCustomer();
}
return $this->_customer;
}
That means that in one of the cases singleton is already implemented/called and in other one - not as the property is already set.
The correct way to work with quote/customer/cart in order to get always the correct data is always to use the singleton pattern.
So using this:
$customer = Mage::getSingleton('customer/session')->getCustomer();
always guarantee that you get the correct customer in that session. And as may be you know singleton pattern is based on registry pattern in app/Mage.php:
public static function getSingleton($modelClass='', array $arguments=array())
{
$registryKey = '_singleton/'.$modelClass;
if (!self::registry($registryKey)) {
self::register($registryKey, self::getModel($modelClass, $arguments));
}
return self::registry($registryKey);
}
and looking at app/Mage.php:
public static function register($key, $value, $graceful = false)
{
if (isset(self::$_registry[$key])) {
if ($graceful) {
return;
}
self::throwException('Mage registry key "'.$key.'" already exists');
}
self::$_registry[$key] = $value;
}
...
public static function registry($key)
{
if (isset(self::$_registry[$key])) {
return self::$_registry[$key];
}
return null;
}
you can see that Magento checks is it is already set. If so, Magento will either throw an Exception, which is the default behavior or return null.
Hope this will help you to understand the issue you face.
I have sorted this out. I have moved the code from footer.phtml to head.phtml and it's working fine now.Values are not changing anymore. If anyone know the logic behind please post and I will change my answer. So far this is working.

Resources