Laravel Eloquent many-to-many attach - php

I am new to laravel, and trying to build a photo album with it.
My problem is that i use the attach function to insert the user id and group id to my database, it works okay, but in the documentation it says this about the attach function
For example, perhaps the role you wish to attach to the user already
exists. Just use the attach method:
So i wanted to use it the same way, if the album_id already exist just update it, other wise insert thr new one, but my problem is it always insters, it does not checks if the album_id already exsits
My model
class User extends Eloquent
{
public static $timestamps = false;
public function album()
{
return $this->has_many_and_belongs_to('album', 'users_album');
}
}
Post function
public function post_albums()
{
$user = User::find($this->id);
$album_id = Input::get('album');
$path = 'addons/uploads/albums/'.$this->id.'/'. $album_id . '/';
$folders = array('path' => $path, 'small' => $path. 'small/', 'medium' => $path. 'medium/', );
if (! is_dir($path) )
{
foreach ($folders as $folder)
{
#mkdir($folder, 0, true);
}
}
$sizes = array(
array(50 , 50 , 'crop', $folders['small'], 90 ),
array(164 , 200 , 'crop', $folders['medium'], 90 ),
);
$upload = Multup::open('photos', 'image|max:3000|mimes:jpg,gif,png', $path)
->sizes( $sizes )
->upload();
if($upload)
{
$user->album()->attach($album_id);
return Redirect::back();
}
else
{
// error show message remove folder
}
}
Could please someone point out what im doing wrong? Or i totally misunderstod the attach function?

I believe you have misunderstood the attach function. The sync function uses attach to add relationships but only if the relationship doesn't already exist. Following what was done there, i'd suggest pulling a list of id's then only inserting if it doesn't already exist in the list.
$current = $user->album()->lists( 'album_id' );
if ( !in_array( $album_id, $current ) )
{
$user->album()->attach( $album_id );
}
On a side note I'm going to suggest that you follow the default naming convention from laravel. The relationship method should be $user->albums() because there are many of them. The pivot table should also be named 'album_user'. You will thank yourself later.

Contains method of Laravel Collections
The laravel collections provides a very useful method 'contains'. It determine if a key exists in the collection. You can get the collection in your case using $user->album. You can note the difference that album is without paranthesis.
Working code
Now all you had to do is use the contains method. The full code will be.
if (!$user->album->contains($album_id)
{
$user->album()->attach($album_id);
}
Its more cleaner and 'Laravel' way of getting the required solution.

Thanks #Collin i noticed i misunderstand i made my check yesterday
$album = $user->album()->where_album_id($album_id)->get();
if(empty($album))
{
$user->album()->attach($album_id);
}

Related

How to check duplicate title and not save to database in laravel

i have a problem that when i get data from other api and want if same title wont save to api. Each time getting data from the api is 20 and want to save it to the database without duplicate. Please help me. Thank you very much!!!
public function getTitle($title){
$title = $this->posts->where('title', $title)->get();
return $title;
}
public function getApi(Request $request){
$url = "https://newsapi.org/v2/top-headlines?sources=techcrunch&apiKey=87384f1c2fe94e11a76b2f6ff11b337f";
$data = Http::get($url);
$item = json_decode($data->body());
$i = collect($item->articles);
$limit = $i->take(20); // take limited 5 items
$decode = json_decode($limit);
foreach($decode as $post){
$ite = (array)$post;
$hi = $this->getTitle($ite['title']);
dd($ite['title'], $hi);
if($ite['title']==$hi){
dd('not save');
}
else{
dd('save');
}
//dd($hi, $ite['title']);
// create post
$dataPost = [
'title'=>$ite['title'],
'description'=>$ite['description'],
'content'=>$ite['content'],
'topic_id'=>'1',
'post_type'=>$request->type,
'user_id'=>'1',
'enable'=>'1',
'feature_image_path'=>$ite['urlToImage']
];
//dd($dataPost);
//$this->posts->create($dataPost);
}
return redirect()->route('posts.index');
}
You can use first or create for saving data in database if title name is new. using firstOrNew you dont have to use any other conditions
for example:-
$this->posts->firstOrCreate(
['title' => $ite['title']],
['description'=>$ite['description'],
'content'=>$ite['content'],
'topic_id'=>'1',
'post_type'=>$request->type,
'user_id'=>'1',
'enable'=>'1',
'feature_image_path'=>$ite['urlToImage']]);
firstOrNew:-
It tries to find a model matching the attributes you pass in the first parameter. If a model is not found, it automatically creates and saves a new Model after applying any attributes passed in the second parameter
From docs
If any records exist that match your query's constraints, you may use
the exists and doesntExist methods
if($this->posts->where('title', $title)->doesntExist())
{
// save
} else {
// not save
}

Passing variable from controller to model functions

Hi Im stucked with this for 4 days now, i have read massive amount of related articles, but seems nothing is working.
First i will explain my idea, give you some code and then i will tell what i have tried.
Idea. I have 2 separate db tables 'project' and 'pictures'. In project table i put info about projects (id, title, pubdate, template etc.) and i need to assign some pictures to that project so on pictures table i have "project_id" column which is relating those two. In my controller i have function project_picture_proccess($id) this $id is changing according to which project im clicking on and that is how all related pictures are fetched. problem is i cant pass that current project $id to upload model (this is for setting upload path for current project).
This is my controller:
class project extends Admin_Controller{
public function __construct(){
parent::__construct();
$this->load->model('project_m');
$this->load->model('project_pictures_m');
//here is index() function and some other (not related to my problem)
public function project_picture_proccess($id = NULL){
//$id variable is from current project im editing
//and changes according to project id
$this->load->model('upload_m');
if ($id) {
//search for directory in upload path with current project slug name
//and if there isnt any it will create it and add proper rights
$this->project_m->search_and_create_dir($id);
//function get_images is fetching all images that current project has
$this->data['project_images'] = $this->project_m->get_images($id);
//this function is setting upload config (setting upload path
//based on directory created in above)-its working var_dump'ed it
$this->upload_m->set_upload_config($id);
}
if($this->input->post('upload')){
//do_multi_upload works on multiple images just fine
//but i cant make it to get outcome of set_upload_config function
//so i can set upload_path very manualy and everything i upload is in mess
$this->upload_m->do_multi_upload();
//redirection function does not work because it is not getting
//any project_m data and i have lost all project_m related data in my view
//its title of project im editing and its id
redirect('admin/project/project_picture_proccess');
}
$this->data['error'] = array('error' => '');
$this->data['project'] = $this->project_m->get($id);
//function get_images is fetching all unsorted images uploaded in
//the path created above for further proccessing
$this->data['unsorted_img'] = $this->upload_m->get_unsorted();
$this->data['subview'] = 'admin/project/picture_proccess';
$this->load->view('admin/main', $this->data);
}
and here is my model:
class Upload_m extends MY_model{
protected $pic_path;
protected $_primary_filter = 'intval';
protected $_table_name = 'projects';
function Upload_m(){
parent::__construct();
$this->pic_path = realpath(APPPATH . '../img/');
function get_slug($id = NULL){
if($id !=NULL){
$filter = $this->_primary_filter;
$id = $filter($id);
$result = mysql_query('SELECT slug FROM ' . $this->_table_name . ' WHERE id=' . $id . ' limit 1');
$data = mysql_fetch_row($result);
$name = array_shift($data); //array - need to be string
return $name;
}else return;
}
function set_upload_config($id){
if($id == NULL) return;
$slug = $this->get_slug($id);
$upload_config = array(
'allowed_types' => 'jpg|gif|jpeg|png|bmp',
'upload_path' => $this->pic_path . $slug . '/',
'max_size' => 0
);
return $upload_config;
}
function do_multi_upload(){
$this->load->library('upload');
$this->upload->initialize($this->set_upload_config());
if($this->upload->do_multi_upload('userfile')){
var_dump($this->upload->get_multi_upload_data());
return;
}else echo "Error at uploading";
}
Here is what i have tried:
moving if($this->input->post...) method inside if($id) scope and
nothing is passed to view and noting is passed to model
tried like every single solution on stackoverflow: these very common tip was $this->load->model('upload_m', $id) <-
returning null when var_dump($id) tested on function
do_something($id){var_dump($id); die;}
i have made global variables and tried to fetch them by get_class_vars
i have rewrite system/core/libraries/model function to accept variable on do_multi_upload($field<-this is default,
$var=NULL<-optional)
i have tried to put those variables in __construct() function like __construct($var)
i have tried to change name of that $id to $var and put it inside if($this->input->post('upload') && $var === $id) and tried to use $var in model. didnt work this approach
I am so desperate this has taken too too much my time. My goal is to pass $id to upload function to set correct upload path and save data into database url (taken from upload_path), project_id ->taken from passed $id. Any help will be appreciated even if its a suggestion how to change my structure so i could still meet my goals.

Sending Data to 2 MySQL tables - FuelPHP / PHP

I am adapting StationWagon (FuelPHP app) and so far it's working really well.
I have adapted it (with some help) to allow multiple images to be uploaded to the server. This is also working great.
However, I am thinking it would make more sense if I had 2 Tables: 1) Articles and 2) ArticleImages. I would use a Foreign Key to associate the Images with the Article. So when publishing an article it would add the article data to 'Articles' table and move each image to a new row in 'ArticleImages'.
So ultimately my 'ArticleImages' table could be:
ID | ImageURL | ArticleID
Portion of my 'articles.php' controller:
<?php
public function action_add()
{
$val = Model_Article::validate('add_article'); //<-- maybe its just me but i never saw any similar to this in fuelphp sorry about this if im wrong
// if your form validation is okay than continue with everyhing else
if ($val->run())
{
$article = Model_Article::forge();
// Custom configuration for this upload
$config = array(
'path' => DOCROOT.DS.'images',
'randomize' => true,
'ext_whitelist' => array('img', 'jpg', 'jpeg', 'gif', 'png'),
);
Upload::process($config);
// if a valid file is passed than the function will save, or if its not empty
if (Upload::is_valid())
{
// save them according to the config
Upload::save();
//if you want to save to tha database lets grab the file name
$value = Upload::get_files();
foreach($value as $files) {
print_r($files);
}
$article->filename = $value[0]['saved_as'];
}
$status = (Input::post('save_draft') ? 0 : 1);
if ( ! $val->input('category_id'))
{
$category_id = null;
}
else
{
$category_id = $val->validated('category_id');
}
$article->user_id = $this->user_id;
$article->category_id = $category_id;
$article->title = $val->validated('title');
$article->body = $val->validated('body');
$article->published = $status;
if ($article->save())
{
Session::set_flash('success', 'Article successfully added.');
}
else
{
Session::set_flash('error', 'Something went wrong, '.
'please try again!');
}
Response::redirect('articles/add');
}
$this->template->title = 'Add Article';
$this->template->content = View::forge('articles/add')
->set('categories', Model_Category::find('all'), false)
->set('val', Validation::instance('add_article'), false);
}
/* End of file articles.php */
You are trying to make a relation between Articles and ArticleImages. An ArticleImage belongs to an Article, while an Article has many ArticleImages. FuelPHP has functionality built in for what you are trying to achieve. Have a look at the FuelPHP docs on its Object Relational Mapper, especially the Belongs To and Has Many relations.
So when i made the code for you back a few days a go you only requested, one file input.
And no offense but you are doing it all wrong...
foreach($value as $files) {
print_r($files);
}
$article->filename = $value[0]['saved_as'];
should be
foreach($value as $files) {
$articleimg = Model_Articleimages::forge();
$articleimg->image_row_name = $files['saved_as']
}
To get you to understand
what you did here, $value = Upload::get_files(); yes this gets all the elements but since you need to loop trouh the elements you dont need it
Second
this $value[0]['saved_as'] only grabs the first image name, just the first one, and since you are in a loop now you need to refer to the $files variable as i shown you in the above example, just an example

Entity CRUD operations - Edit (update) issues

I'm tryin to learn few things about Zend Framework and I got stucked on so simple operation like 'Edit' DB entry.
I've got list of contacts in MySQL db and my intention is to fill form with information from one row, edit it and save it back to db (update statement). I tried almost everthing that came into my mind, checked out google and book about ZF, but there is some problem all the time. At this moment, when I want to do update, zf and mysql will create new db entry with new id and edited information filled in, but that is not what i want to do obviously because instead of one updated entry in DB I've got two - old one and new one with updated information.
Here are the importat parts of my code...please have a look at it, I can't figure out what I'm missing here.
part of indexcontroller:
public function createcontactAction()
{
$createContactForm = $this->_helper->_formLoader('addContact');
$this->view->addContactForm = $createContactForm;
}
public function editcontactAction()
{
$id = $this->getRequest()->getParam('id');
$contactModel = new Application_Model_Contacts();
$contactRow = $contactModel->find($id)->current();
$addContactForm = $this->_helper->formLoader('addContact');
if ($this->getRequest()->isPost() && $this->getRequest()->getPost('send', false) !== false) {
if ($addContactForm->isValid($this->getRequest()->getPost())) {
$contactRow->setFromArray($addContactForm->getValues());
$contactRow->save();
$this->_redirect('/index/editcontact/id/' . $contactRow->id);
}
} else {
$addContactForm->populate($contactRow->toArray());
}
$this->view->addContactForm = $addContactForm;
}
public function savecontactAction()
{
$form = $this->_helper->formLoader('addContact');
if ($this->getRequest()->isPost() && $this->getRequest()->getPost('send', false) !== false) {
if ($form->isValid($this->getRequest()->getPost())) {
$contactModel = new Application_Model_Contacts();
$contactRow = $contactModel->createRow($form->getValues());
$contactRow->save();
$this->_redirect('/index/editcontact/id/' . $contactRow->id);
}
}
$this->view->form = $form;
}
form - parts that matters:
class Application_Form_AddContact extends Zend_Form
{
public function init()
{
$this->setAction('/index/savecontact');
$this->setMethod(Zend_Form::METHOD_POST);
$this->setAttrib('id', 'index_savecontact');
$contactFirstName = new Zend_Form_Element_Text('first_name', array('size'=>32, 'maxlength'=>64, 'label'=>'Křestní', 'required'=>false));
$contactLastName = new Zend_Form_Element_Text('last_name', array('size'=>32, 'maxlength'=>64, 'label'=>'Přímění', 'required'=>true));
.
.
.
$contactNotes = new Zend_Form_Element_Textarea('notes', array('cols'=>32, 'rows'=>1, 'label'=>'Poznámky', 'required'=>false));
$contactSend = new Zend_Form_Element_Submit('send', array('label'=>'Odeslat'));
$this->addElements(array ($contactFirstName, $contactLastName, $contactStreet, $contactHouseNumber, $contactCity, $contactZipCode, $contactCountry,
$contactPhoneNumber, $contactMobileNumber, $contactEmail, $contactWebPage, $contactCrn, $contactVat, $contactNotes, $contactSend));
Thank you very much!
(If theres anything more you could need to help me with this just ask for it)
EDIT:
heres model for contacts:
class Application_Model_Contacts extends Zend_Db_Table_Abstract
{
protected $_name = 'contacts';
protected $_primary = 'id';
}
I'm a bit rusty with regards to Zend_Db_Table and Zend_Db_Table_Row (I'm assuming that is what your model uses), but my bet would be that you are missing the Primary Key (PK) in your $contactRow - I'm guessing you probably don't supply it via the form as I see you get it through GET. So just set the id to $id in your $contactRow and you should be fine.
In editcontactAction(), before $contactRow->save();, add : $contactRow->id = $id. If your row doesn't have a id specified, save() can't update.
You're trying to get an update without providing the id of the row you want to update. The data used for the query is $form->getValues() but the form doesn't seem to contain the id of the contact. Add the id as a hidden field (with the id as the value) to your form or set it separately with $contactRow->id = $id and it should update instead of insert.

Which is the event listener after doSave() in Symfony?

I've been looking at this event-listeners page http://www.doctrine-project.org/documentation/manual/1_1/pl/event-listeners and I'm not sure which is the listener I have to use to make a change after the doSave() method in the BaseModelForm.class.php.
// PlaceForm.class.php
protected function doSave ( $con = null )
{
...
parent::doSave($con);
....
// Only for new forms, insert place into the tree
if($this->object->level == null){
$parent = Place::getPlace($this->getValue('parent'), Language::getLang());
...
$node = $this->object->getNode();
$method = ($node->isValidNode() ? 'move' : 'insert') . 'AsFirstChildOf';
$node->$method($parent); //calls $this->object->save internally
}
return;
}
What I want to do is to make a custom slug with the ancestors' name of that new place. So if I inserting "San Francisco", the slug would be "usa-california-san-francisco"
public function postXXXXXX($event)
{
...
$event->getInvoker()->slug = $slug;
}
The problem is that I'm inserting a new object with no reference to its parent. After it's saved, I insert it to the tree. So I can't change the slug until then.
I think a Transaction listener could work, but I'm use there is a better way I'm not seeing right now.
thanks!
You are looking at the wrong piece of code. As stated by benlumley, you should manage your slug directly in the model, not in the form. To achieve what you want (a recursive slug) is quite easy using doctrine's Sluggable behavior. You need to implement a getUniqueSlug() into your model so that it gets called by the behavior (it's automatic) and handle your slug specifities in there:
public function getUniqueSlug()
{
$slug = '';
$parent = $this->getParent();
if ($parent->exists())
{
$slug = $this->getParent()->getUniqueSlug().'-';
}
return $slug.$this->getName();
}
What we do here is basically traverse all the ancestors of the current object and append the slugs on the go (replace the getParent() by whatever method you use to retrieve an object's parent.
Firstly, I'd put this into the model rather than the form - that way if the object is ever edited/updated the behaviour would still happen.
In the form though, I'd use updateObject:
function updateObject($values = array()) {
parent::updateObject($values);
// do your stuff
}
In the model (looks like you are using doctrine ...) I'd put this into the postSave() method. As I say, I think its better there than the form.
I had the same problems, and the Doctrine_Record::postInsert(Doctrine_Event $event) method did not work for me. Indeed the node aren't hydrated yet.
I had to overwrite the sfFormObject::doSave method like this:
protected function doSave($con = null)
{
$is_new = $this->isNew();
parent::doSave($con);
$this->doSaveNestedSet($con);
$service = $this->getObject();
if( $is_new and ! $service->getClientId() and $parent = $service->getParent())
{
$service->setClient($parent->getClient());
$service->save();
}
}

Categories