Search Results for: app development

Unity 2D Animation: Top-Down Unity Player Movement

The thing that is so popular in the gaming community is the top-down gaming style (also known as isometric games). This type of perspective is often used by games like RPGs.

In this tutorial, we’re going to learn how to create unity top-down movement without writing the actual player movement script just pure animation and a little code. That’s right! We’re going to use Unity Animation to make solid movements for our Player.

Let’s get started!

Open Unity and create a new project. (Make sure it is a 2D game project.)

Unity 2019 2D Game Development - Getting Started

Video Tutorial

If you prefer watching video tutorials, you may watch the video version of this lesson below:

Getting Started

Before we proceed, we need to download the 2D assets that we can use for this project. If you have your own assets like 2D characters and platform backgrounds, you may skip this step. However, we encourage you to follow along so you’ll understand how we made it to the final output.

Press CTRL + 9 to open the asset store or Window tab > Asset Store

Search for Tiny RPG and download the asset.

Build a Top-Down RPG/Adventure game with Unity3D

If you’re looking for more 2D characters, we highly recommend using these retro characters as they all have the complete animations for most game needs.

After importing the assets, design your own platform using the sprites inside the folder Environment or you may download the following image and use it as a sprite.

Tiny RPG Game with Unity 2019

And here’s how it looks like in the editor:

Unity 2D Game Development Tutorial Series for Beginners

Bringing in the 2D Character

The next thing that we want to do is, of course, bring in our player. To do that, go to Tiny RPG Forest folder, and then Artwork folder, and then the sprites.

Inside the sprites folder, select the hero folder.

2D Animation Sprites with Unity 2019

Notice the content of the hero folder. There you will see all of the action sprites for the player such as attack, idle, and walk. First, let’s open the Idle folder.

Inside the folder, you will see another three different folders for the different directions our player can be facing (front, back, and sides).

We’re going to select the hero-idle-front sprite and let’s drag it into our scene.

Unity 2D Sprite not showing up above the platform

Sprite not showing up error

If somehow you encounter this type of error after you drag your sprite into your scene. Just make sure that your background or platform has an Order in Layer value of -1.

Unity 2D Sprite not showing up in the scene error

Awesome! Now that the character is in the scene, let’s rename it to Player so we know that he is the player that we’re going to control.

Now that everything is set up, let’s start making our player move around.

There are two ways to control our player. The first option is to add the actual movement to our player and the second option is to add Animation based on that movement. Both are equally possible but the first option will require too much programming and we don’t want that in this tutorial. We want to control our player with little programming possible.

Now, let’s begin by adding Rigidbody 2D to our player. Select your Player, go to the Inspector and click Add Component. Scroll down until you find Physics 2D and then, select Rigidbody 2D.

You may also use the search bar and type Rigidbody 2D instead.

Unity 2D Physics Rigidbody Component

In the Rigidbody 2D component, set the value of Gravity Scale to 0 and under the constraints variable check the Freeze Rotation Z.

Player Movement

Create a new C# script and name it PlayerMovement.cs.

To start the code, let’s declare the first three variables.

public float movementSpeed = 1f;   //Movement Speed of the Player
public Vector2 movement;           //Movement Axis
public Rigidbody2D rigidbody;      //Player Rigidbody Component
Code language: C# (cs)

The next thing that we’re going to do is to give our player’s rigidbody component to the rigidbody variable that we’ve created. To do that, use the GetComponent function.

// Start is called before the first frame update
void Start()
{
    rigidbody = this.GetComponent<Rigidbody2D>();
}
Code language: C# (cs)

Don’t forget to save your code.

Now, what the code above does is, it initialize the rigidbody variable as soon as we start our game. Because of that, we can access the physics to control our player.

If you look at your code, you will see that there is an Update function. If we use the update function, we can control the character in every frame possible. However, update function can be unreliable due to its constantly changing frame rate and we don’t want that for our player but instead, we’re going to use the FixedUpdate function.

Inside your PlayerMovement script, add the fixed update function just underneath the update function.

void FixedUpdate() {}
Code language: C# (cs)

The reason why we’re doing it like this is because we have to make sure that there won’t be any delays to our player movement. FixedUpdate works exactly the same thing as the update function. The only difference is, the update function is depending on the frame rate. So for example, the frame rate goes down to 10 frames per second, expect the player to have a little choppiness to its animation.

FixedUpdate function, however, is by default, called 50 frames per second which are recommended especially if you’re working with Physics.

We’re still going to use the update function for registering the player input.

Let’s work on that.

The next thing that we’re going to do is to find out what exactly our player is currently pressing. Is it WASD? Arrow keys? Touch pad? etc.

In that case, we’re going to use Input.GetAxisRaw() function.

To simply explain what the function above does. Try to imagine the cartesian plane. Both axes will have a range of value between negative one and positive one. If the player presses the left arrow key, the function will return a value of negative one. If the player presses the up key, the function will return a value of positive one. The same goes to the rest of the keys, the function will return you a value depending on which keys your pressing.

Now, to retrieve the value for the x-axis, we’re going to use the horizontal input and to retrieve the value for the y-axis, we’re going to use the vertical input.

// Update is called once per frame
void Update()
{
    movement.x = Input.GetAxisRaw("Horizontal");
    movement.y = Input.GetAxisRaw("Vertical");
}
Code language: C# (cs)

Now that we have the value of our inputs, we can now control the position of our player.

To do that, copy the following code for the FixedUpdate function.

private void FixedUpdate()
{
    rigidbody.MovePosition(rigidbody.position + movement * movementSpeed * Time.fixedDeltaTime);
}
Code language: C# (cs)

What is happening in that code? Simple, we will move our player depending on its current position and then add the value of our input multiplied to the movement speed and fixedDeltaTime.

What is fixedDeltaTime? The interval in seconds at which physics and other fixed frame rate updates.

Save your code and go back to your editor.

If you try to run your game you will see that you can now actually move your player. However, there is no animation yet. Our player moves around but facing towards us. Creepy!

Let’s fix that.

Player Animation

Unit2D Animation for Player Character

To start animating your player, go to Window tab, Animation, and select Animation. You may also just press CTRL + 6 for a shortcut.

Make sure it is Animation and not Animator, some people get confused by that.

Now, with your player game object selected, click create.

You may go to your project root folder and create a new folder for the Animations, just for the sake of file structure.

Name the animation Idle.anim.

Next, go back to the hero folder that contains all the idle sprites and drag the hero-idle-front sprite in the animation window.

Animating 2D Characters using Unity 2019 Animation

Next, create a new Animation clip by clicking the dropdown with the name of your current animation and click Create New Clip

Creating Animations for 2D Characters with Unity3D

Name your new animation clip Walk_Up.anim and then go to the hero-walk-back folder and drag all the sprites into the animation window.

To select multiple sprites, simply click the first sprite, hold the shift key, and then click the last sprite.

Multiple Sprites Animation with Unity3D

If you try to play the animation, you will see that the animation is playing very fast. To fix that, change the value of Samples to a lower value like 15.

Now, create the animation clips and do the same process for the Walk_Down and Walk_Side animation clips.

If you have noticed, we only have animations for walking right and we don’t have animations for walking left. Now, there are many ways to solve this issue. First is to use Photoshop and manually flip all the sprites so it will face the left axis. Cool! But we’re not going to do that, but instead, we’re going to use our PlayerMovement script to flip the animation so it will look like he’s moving to the left.

Animation Component

After we’ve created all the animations, our player got a new component and that is the Animation.

If you go to Window tab, then Animation, and Animator, you will see something like this.

Unity 2019 Animator Window

As you can see, there is our Idle animation, walk_up, walk_down, and walk_side animation. Think of this as a playlist of animations but in order for us to play them, we need to create a certain rule for each animation. For example, if the player is not pressing any keys, then play the idle animation. If the player is pressing the arrow keys or WASD keys then play the walking animation.

In order for us to do that, we need to create a parameter. Go to the top-left corner of the Animator window and click Parameters.

Unity3D Creating Paramaters in Animator window

Right beside the search bar, there is a plus button. Press that and select float. Let’s name it Horizontal.

Again, do the same thing. Create a new parameter and select float and name it Vertical this time.

Next, delete all the animations in the Animator except the idle and walk-side animation.

Deleting Animations in Animator with Unity3D

Animator: Blend Tree

Now that we have the parameters ready, we can now use them to control a blend tree. Blend trees are used to smoothly transition from one animation state to another.

To create a blend tree, go to Animator, right-click, create state and select From New Blend Tree. Go to the inspector and rename it to Vertical.

Next, double click the blend tree and select the node (Blend Tree).

Go to the inspector, make sure the parameter used is vertical.

Then add a new motion field.

Next, find your walk-down animation and drag it to the motion field.
Create another motion field but this time for the walk-up animation.

Awesome! Let’s uncheck the Automate Thresholds in the inspector and modify the thresholds of our animations.

For walk-up animation, the threshold would be 1 and the threshold for the walk-down animation would be -1. We’re doing this because we’re following the values that are given by our input.

Unity3D Animations using Blend Trees

Next, go back to the base layer and create a new parameter and call it speed.

Now, we’re going to create a transition between our animations so go ahead and right-click to the idle animation and select make transition. Then select the Vertical blend tree node.

We’re going to do this as well to our Vertical blend tree and walk-side animation, right-click, select make transition and choose the idle animation and vice versa. Awesome!

Transition: Idle to Vertical Blend Tree

Unity3D Animations using Blend Trees

Now, click on the transition that points to our vertical blend tree.

In the inspector, make sure that the Has exit time is unchecked and inside the settings, the transition duration is set to 0.

Next, In the conditions section, click the plus button and select speed and then choose Greater than the value 0.01

Transition: Vertical Blend Tree to Idle

Unity3D Animations using Blend Trees

Click on the transition that points to our idle animation.

In the inspector, make sure that the Has exit time is unchecked as well and inside the settings, the transition duration is set to 0.

In the conditions section, click the plus button and select speed and then choose Less than the value 0.01

Transition: Idle to Walk-side Animation

Unity3D Animations using Blend Trees

Click on the transition that points to our walk-side animation.

In the inspector, make sure that the Has exit time is unchecked as well and inside the settings, the transition duration is set to 0.

In the conditions section, click the plus button and select horizontal and then choose Greater than the value 0.01

Transition: Walk-side Animation to Idle

Unity3D Animations using Blend Trees

Click on the transition that points from walk-side animation to our idle animation.

In the inspector, make sure that the Has exit time is unchecked and inside the settings, the transition duration is set to 0.

In the conditions section, click the plus button and select horizontal and then choose Less than the value 0.01

Unity3D Animator Window with Transitions

Scripting Animator Controller

After setting up your animator component, we can now send the value of our input into our animator’s parameters and control our animation state.

To do that, we need to create a new variable.

Go back to your PlayerMovement.cs and add the following variable.

public Animator anim;
public float hf = 0.0f;
public float vf = 0.0f;
Code language: C# (cs)

And inside the start function, add the following code.

anim = this.GetComponent<Animator>();
Code language: C# (cs)

And here’s the new code for the update function

// Update is called once per frame
void Update()
{
    movement.x = Input.GetAxisRaw("Horizontal");
    movement.y = Input.GetAxisRaw("Vertical");

    hf = movement.x > 0.01f ? movement.x : movement.x < -0.01f ? 1 : 0;
    vf = movement.y > 0.01f ? movement.y : movement.y < -0.01f ? 1 : 0;
    if (movement.x < -0.01f)
    {
        this.gameObject.transform.localScale = new Vector3(-1, 1, 1);
    } else
    {
        this.gameObject.transform.localScale = new Vector3(1, 1, 1);
    }

    anim.SetFloat("Horizontal", hf);
    anim.SetFloat("Vertical", movement.y);
    anim.SetFloat("Speed", vf);
}
Code language: C# (cs)

Save your script and go back to your editor and see the results!

Normalizing Player Movement

There are instances that when you move your player up and right at the same time, the movement speed multiplies by two.

To ensure that all movement speeds are equally balanced, add the following code just below where we used the Input.GetAxisRaw();

movement = movement.normalized;
Code language: C# (cs)

Closing Line

Congratulations! You just made your top-down player movement.

Do you have questions or suggestions? Let us know in the comments below and we’ll try our best to communicate with you.

If you’re looking for the next step, then we have already published a new tutorial on how to make this player character attack with animation.

Read next: How To Do 2D Melee Combat in Unity 2019

Disclosure: This article may contain affiliate links, which means we may receive a commission if you click a link and purchase something that we have recommended.

Shopify Tutorial PHP: Update Product’s Collection with Collect API

So you are developing a Shopify app where merchants can easily customize their product’s title, description, and even the prices. However, you forgot one thing and that is the collections.

How do you actually update the product’s collections?

In this Shopify tutorial, we’ll learn how to update the product’s collections using Collect REST API and PHP.

Video Tutorial

If you prefer to learn through a video tutorial, you may watch the following video.

Getting started

To get started, we’ll assume that you have followed our Shopify app tutorial series and you are using the same repository/client that we are using here.

Now, for this tutorial, we’ll only need the following three files.

  • index.php
  • ajax.php
  • product.php

In our product.php, we should have the following code:


<div class="card-columns">
<?php
$products = shopify_call($token, $shop_url, "/admin/api/2020-07/products.json", array(), 'GET');
$products = json_decode($products['response'], JSON_PRETTY_PRINT);

foreach ($products as $product) {
	foreach ($product as $key => $value) {
		$images = shopify_call($token, $shop_url, "/admin/api/2020-07/products/" . $value['id'] . "/images.json", array(), 'GET');
		$images  = json_decode($images['response'], JSON_PRETTY_PRINT);
		?>

			<div class="card" product-id="<?php echo $value['id']; ?>">
				<img class="card-img-top" src="<?php echo $images['images'][0]['src']; ?>" alt="Card image cap">
				<div class="card-body">
					<h5 class="card-title"><?php echo $value['title']; ?></h5>
				</div>
			</div>

		<?php
	}
}
?>
</div>

<!-- Modal -->
<div class="modal fade" id="productsModal" tabindex="-1" role="dialog" aria-labelledby="productsModalLabel" aria-hidden="true">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        <form action="ajax.php" id="productForm">
        	<div class="form-group">
        		<label for="productName">Product Title</label>
        		<input type="text" class="form-control" id="productName" name="productName">
        	</div>
        	<div class="form-group">
        		<label for="productDescription">Product Description</label>
        		<textarea class="form-control" id="productDescription" name="productDescription" rows="7"></textarea>
        	</div>
        	<div class="form-group">
        		<select class="custom-select" id="productCollection" name="productCollection" multiple>
              <?php
                $custom_collections = shopify_call($token, $shop_url, "/admin/api/2020-07/custom_collections.json", array(), 'GET');
                $custom_collections = json_decode($custom_collections['response'], JSON_PRETTY_PRINT);

                foreach ($custom_collections as $custom_collection) {
                  foreach ($custom_collection as $key => $value) {
                    ?>
                      <option value="<?php echo $value['id']; ?>"><?php echo $value['title']; ?></option>
                    <?php
                  }
                }

                $smart_collections = shopify_call($token, $shop_url, "/admin/api/2020-07/smart_collections.json", array(), 'GET');
                $smart_collections = json_decode($smart_collections['response'], JSON_PRETTY_PRINT);

                foreach ($smart_collections as $smart_collection) {
                  foreach ($smart_collection as $key => $value) {
                    ?>
                      <option value="<?php echo $value['id']; ?>"><?php echo $value['title']; ?></option>
                    <?php
                  }
                }
              ?>
        		</select>
        	</div>
        </form>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
        <button type="button" class="btn btn-primary" id="SaveProduct" product-id=''>Save changes</button>
      </div>
    </div>
  </div>
</div>

Code language: HTML, XML (xml)

In our index.php, we should have the following code:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
    <title>WeeklyHow Shopify App</title>
  </head>
  <body>

	<div class="container">
		<?php
		include_once("inc/functions.php");
		include_once("inc/mysql_connect.php");
		include_once("header.php");
		include_once("products.php");
		?>
	</div>
    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>

    <script>
      var shop = '<?php echo $shop_url; ?>';

      $('div[product-id]').on('click', function (e) {
        $.ajax({
          method: 'POST',
          data: {
            url: shop,
            id: $(this).attr('product-id'),
            type: 'GET'
          },
          url:'ajax.php',
          dataType: 'json',
          success:function(json){
            console.log(json);

            $('#productName').val(json['title']);
            $('#productDescription').val(json['description']);

            $('#productCollection option').each(function(i) {
              var optionCollection = $(this).val();

              json['collections'].forEach(function(productCollection) {
                if(productCollection['id'] == optionCollection) {
                  $('#productCollection option[value=' + optionCollection + ']').attr('selected', 'selected');
                }

              });
            });


            $('#SaveProduct').attr('product-id', json['id']);
            $('#productsModal').modal('show');
          }   
        }); 
      });


      $('#productsModal').on('hide.bs.modal', function() {
        $('#SaveProduct').attr('product-id', '');
        $('#productCollection').val([]);
      });

      $('#SaveProduct').on('click', function() {
        var productID = $(this).attr('product-id');

        $.ajax({
          method: 'POST',
          data: {
            url: shop,
            id: productID,
            product: $('#productForm').serialize(),
            type: 'PUT'
          },
          url:'ajax.php',
          dataType: 'html',
          success:function(json){
            console.log(json);
          }   
        }); 
      });
    </script>
  </body>
</html>
Code language: HTML, XML (xml)

And for our ajax.php, we should have the following code:

<?php
require_once("inc/functions.php");
require_once("inc/mysql_connect.php");

$id = $_POST['id'];
$shop_url = $_POST['url'];

$sql = "SELECT * FROM shops WHERE shop_url='" . $shop_url . "' LIMIT 1";
$check = mysqli_query($conn, $sql);

if(mysqli_num_rows($check) > 0) {
	$shop_row = mysqli_fetch_assoc($check);

	$token = $shop_row['access_token'];

	if($_POST['type'] == 'GET') {
		$products = shopify_call($token, $shop_url, "/admin/api/2020-07/products/" . $id . ".json", array(), 'GET');
		$products = json_decode($products['response'], JSON_PRETTY_PRINT);

		$id = $products['product']['id'];
		$title = $products['product']['title'];
		$description = $products['product']['body_html'];
		$collections = array();

		$custom_collections = shopify_call($token, $shop_url, "/admin/api/2020-07/custom_collections.json", array("product_id" => $id), 'GET');
		$custom_collections = json_decode($custom_collections['response'], JSON_PRETTY_PRINT);

		foreach ($custom_collections as $custom_collection) {
			foreach ($custom_collection as $key => $value) {
				array_push($collections, array("id" => $value['id'], "name" => $value['title']));
			}
		}

		$smart_collections = shopify_call($token, $shop_url, "/admin/api/2020-07/smart_collections.json", array("product_id" => $id), 'GET');
		$smart_collections = json_decode($smart_collections['response'], JSON_PRETTY_PRINT);

		foreach ($smart_collections as $smart_collection) {
			foreach ($smart_collection as $key => $value) {
				array_push($collections, array("id" => $value['id'], "name" => $value['title']));
			}
		}

		echo json_encode(
			array(
				"id" => $id,
				"title" => $title,
				"description" => $description,
				"collections" => $collections
			)
		);

	} else if( $_POST['type'] == 'PUT' ) {
		$productData = array();
		$productData = proper_parse_str($_POST['product']);

		$array = array("product" => array("title" => $productData['productName'], "body_html" => $productData['productDescription']));

		$products = shopify_call($token, $shop_url, "/admin/api/2020-07/products/" . $id . ".json", $array, 'PUT');
	}
	
}
?>
Code language: PHP (php)

The scripts above should allow us to edit and save a new title and description for any of the products that we select below.

shopify products grid design

Now, what we want to do is when we select a product. A bootstrap modal should appear and allow us to select new collections for our product.

updating shopify products in bootstrap modal

Updating the product’s collection

There are two ways to update the product’s collection. One is to use the Collection API, delete the collection attached to the product, and create the deleted collection and assign all of the previous products assigned to the collection except that product that we selected. Two is to use Collect API.

Obviously, the first option is the worse way of updating the product’s collection. Why? Because it’s ugly and it’s making too many unnecessary API calls.

However, if you use collect API instead, all we needed to do is to get the collect attached to the product that we selected, delete the collect, and create a new collect with the new collection. Simple as that!

Let’s start by getting all of the collect from collect API. Open the ajax.php and update its code to the following:

<?php
require_once("inc/functions.php");
require_once("inc/mysql_connect.php");

$id = $_POST['id'];
$shop_url = $_POST['url'];

$sql = "SELECT * FROM shops WHERE shop_url='" . $shop_url . "' LIMIT 1";
$check = mysqli_query($conn, $sql);

if(mysqli_num_rows($check) > 0) {
	$shop_row = mysqli_fetch_assoc($check);

	$token = $shop_row['access_token'];

	if($_POST['type'] == 'GET') {
		$products = shopify_call($token, $shop_url, "/admin/api/2020-07/products/" . $id . ".json", array(), 'GET');
		$products = json_decode($products['response'], JSON_PRETTY_PRINT);

		$id = $products['product']['id'];
		$title = $products['product']['title'];
		$description = $products['product']['body_html'];
		$collections = array();

		$custom_collections = shopify_call($token, $shop_url, "/admin/api/2020-07/custom_collections.json", array("product_id" => $id), 'GET');
		$custom_collections = json_decode($custom_collections['response'], JSON_PRETTY_PRINT);

		foreach ($custom_collections as $custom_collection) {
			foreach ($custom_collection as $key => $value) {
				array_push($collections, array("id" => $value['id'], "name" => $value['title']));
			}
		}

		$smart_collections = shopify_call($token, $shop_url, "/admin/api/2020-07/smart_collections.json", array("product_id" => $id), 'GET');
		$smart_collections = json_decode($smart_collections['response'], JSON_PRETTY_PRINT);

		foreach ($smart_collections as $smart_collection) {
			foreach ($smart_collection as $key => $value) {
				array_push($collections, array("id" => $value['id'], "name" => $value['title']));
			}
		}

		echo json_encode(
			array(
				"id" => $id,
				"title" => $title,
				"description" => $description,
				"collections" => $collections
			)
		);

	} else if( $_POST['type'] == 'PUT' ) {
		$productData = array();
		parse_str($_POST['product'], $productData);

		$array = array("product" => array("title" => $productData['productName'], "body_html" => $productData['productDescription']));

		$products = shopify_call($token, $shop_url, "/admin/api/2020-07/products/" . $id . ".json", $array, 'PUT');

		$collects = shopify_call($token, $shop_url, "/admin/api/2020-07/collects.json", array('product_id' => $id), 'GET');
		$collects = json_decode($collects['response'], JSON_PRETTY_PRINT);

		foreach ($collects as $collect) {
			foreach ($collect as $key => $value) {
				$collects = shopify_call($token, $shop_url, "/admin/api/2020-07/collects/" . $value['id'] .".json", array(), 'DELETE');
			}
		}

	}
	
}
?>
Code language: PHP (php)

In the code above, we first get the collects assigned to our selected product and then loop through each collect using foreach and then delete each collect. These are just basics and you should be able to follow along 🙂

Next, we’ll use POST endpoint to create the new collect.

<?php
require_once("inc/functions.php");
require_once("inc/mysql_connect.php");

$id = $_POST['id'];
$shop_url = $_POST['url'];

$sql = "SELECT * FROM shops WHERE shop_url='" . $shop_url . "' LIMIT 1";
$check = mysqli_query($conn, $sql);

if(mysqli_num_rows($check) > 0) {
	$shop_row = mysqli_fetch_assoc($check);

	$token = $shop_row['access_token'];

	if($_POST['type'] == 'GET') {
		$products = shopify_call($token, $shop_url, "/admin/api/2020-07/products/" . $id . ".json", array(), 'GET');
		$products = json_decode($products['response'], JSON_PRETTY_PRINT);

		$id = $products['product']['id'];
		$title = $products['product']['title'];
		$description = $products['product']['body_html'];
		$collections = array();

		$custom_collections = shopify_call($token, $shop_url, "/admin/api/2020-07/custom_collections.json", array("product_id" => $id), 'GET');
		$custom_collections = json_decode($custom_collections['response'], JSON_PRETTY_PRINT);

		foreach ($custom_collections as $custom_collection) {
			foreach ($custom_collection as $key => $value) {
				array_push($collections, array("id" => $value['id'], "name" => $value['title']));
			}
		}

		$smart_collections = shopify_call($token, $shop_url, "/admin/api/2020-07/smart_collections.json", array("product_id" => $id), 'GET');
		$smart_collections = json_decode($smart_collections['response'], JSON_PRETTY_PRINT);

		foreach ($smart_collections as $smart_collection) {
			foreach ($smart_collection as $key => $value) {
				array_push($collections, array("id" => $value['id'], "name" => $value['title']));
			}
		}

		echo json_encode(
			array(
				"id" => $id,
				"title" => $title,
				"description" => $description,
				"collections" => $collections
			)
		);

	} else if( $_POST['type'] == 'PUT' ) {
		$productData = array();
		parse_str($_POST['product'], $productData);

		$array = array("product" => array("title" => $productData['productName'], "body_html" => $productData['productDescription']));

		$products = shopify_call($token, $shop_url, "/admin/api/2020-07/products/" . $id . ".json", $array, 'PUT');

		$collects = shopify_call($token, $shop_url, "/admin/api/2020-07/collects.json", array('product_id' => $id), 'GET');
		$collects = json_decode($collects['response'], JSON_PRETTY_PRINT);

		foreach ($collects as $collect) {
			foreach ($collect as $key => $value) {
				$collects = shopify_call($token, $shop_url, "/admin/api/2020-07/collects/" . $value['id'] .".json", array(), 'DELETE');
			}
		}


		if( count($productData['productCollection'] ) > 0) {
			for ($i = 0; $i < count($productData['productCollection']); $i++) { 
				if(count($productData['productCollection']) >= 0 && count($productData['productCollection']) <= 1) {
					$value = $productData['productCollection'];
				} else {
					$value = $productData['productCollection'][$i];
				}
				

				$collects = shopify_call($token, $shop_url, "/admin/api/2020-07/collects.json", array('collect' => array('product_id' => $id, 'collection_id' => $value)), 'POST');
			}
		}
	}
	
}
?>
Code language: PHP (php)

In the above code, we need to check if the collections selected by the merchant is more than one. If it is, then we’ll loop through each collections and create the collect using the value of product ID and collection ID.

If you’re wondering why there is a condition inside the loop, it’s because the productData variable can create two-dimensional arrays especially if the length of the array is more than 1. So we need to make sure we get both values regardless of the array length.

With everything saved, we should be able to update the product’s collections. BUT! Unfortunately, the built-in parse_str() function is not going to work if there are two or more query strings with the same name.

For example, let’s say we have the following query string:

https://weeklyhow.com/?id=1&id=2&id=3
Code language: JavaScript (javascript)

If we use the built-in parse string function, the function will only return the last value which is 3.

So if we use the same function, it’ll most likely not create the first collects.

To fix that, we’ll have to use the following function provided by Evan K from php.net:

function proper_parse_str($str) {
  # result array
  $arr = array();

  # split on outer delimiter
  $pairs = explode('&', $str);

  # loop through each pair
  foreach ($pairs as $i) {
    # split into name and value
    list($name,$value) = explode('=', $i, 2);
   
    # if name already exists
    if( isset($arr[$name]) ) {
      # stick multiple values into an array
      if( is_array($arr[$name]) ) {
        $arr[$name][] = $value;
      }
      else {
        $arr[$name] = array($arr[$name], $value);
      }
    }
    # otherwise, simply stick it in a scalar
    else {
      $arr[$name] = $value;
    }
  }

  # return result array
  return $arr;
}
Code language: PHP (php)

You can copy the above function and paste it either on the very top of ajax.php or inside the functions.php.

Once you’re done copying the code, we should then be able to use it instead of the parse_str() function.

<?php
require_once("inc/functions.php");
require_once("inc/mysql_connect.php");

$id = $_POST['id'];
$shop_url = $_POST['url'];

$sql = "SELECT * FROM shops WHERE shop_url='" . $shop_url . "' LIMIT 1";
$check = mysqli_query($conn, $sql);

if(mysqli_num_rows($check) > 0) {
	$shop_row = mysqli_fetch_assoc($check);

	$token = $shop_row['access_token'];

	if($_POST['type'] == 'GET') {
		$products = shopify_call($token, $shop_url, "/admin/api/2020-07/products/" . $id . ".json", array(), 'GET');
		$products = json_decode($products['response'], JSON_PRETTY_PRINT);

		$id = $products['product']['id'];
		$title = $products['product']['title'];
		$description = $products['product']['body_html'];
		$collections = array();

		$custom_collections = shopify_call($token, $shop_url, "/admin/api/2020-07/custom_collections.json", array("product_id" => $id), 'GET');
		$custom_collections = json_decode($custom_collections['response'], JSON_PRETTY_PRINT);

		foreach ($custom_collections as $custom_collection) {
			foreach ($custom_collection as $key => $value) {
				array_push($collections, array("id" => $value['id'], "name" => $value['title']));
			}
		}

		$smart_collections = shopify_call($token, $shop_url, "/admin/api/2020-07/smart_collections.json", array("product_id" => $id), 'GET');
		$smart_collections = json_decode($smart_collections['response'], JSON_PRETTY_PRINT);

		foreach ($smart_collections as $smart_collection) {
			foreach ($smart_collection as $key => $value) {
				array_push($collections, array("id" => $value['id'], "name" => $value['title']));
			}
		}

		echo json_encode(
			array(
				"id" => $id,
				"title" => $title,
				"description" => $description,
				"collections" => $collections
			)
		);

	} else if( $_POST['type'] == 'PUT' ) {
		$productData = array();
		$productData = proper_parse_str($_POST['product']);

		$array = array("product" => array("title" => urldecode($productData['productName']), "body_html" => urldecode($productData['productDescription'])));
		
		$products = shopify_call($token, $shop_url, "/admin/api/2020-07/products/" . $id . ".json", $array, 'PUT');

		$collects = shopify_call($token, $shop_url, "/admin/api/2020-07/collects.json", array('product_id' => $id), 'GET');
		$collects = json_decode($collects['response'], JSON_PRETTY_PRINT);

		foreach ($collects as $collect) {
			foreach ($collect as $key => $value) {
				$collects = shopify_call($token, $shop_url, "/admin/api/2020-07/collects/" . $value['id'] .".json", array(), 'DELETE');
			}
		}


		if( count($productData['productCollection'] ) > 0) {
			for ($i = 0; $i < count($productData['productCollection']); $i++) { 
				if(count($productData['productCollection']) >= 0 && count($productData['productCollection']) <= 1) {
					$value = $productData['productCollection'];
				} else {
					$value = $productData['productCollection'][$i];
				}
				

				$collects = shopify_call($token, $shop_url, "/admin/api/2020-07/collects.json", array('collect' => array('product_id' => $id, 'collection_id' => $value)), 'POST');
			}
		}
	}
	
}
?>
Code language: PHP (php)

Now, query strings are encoded to URL text formatting that’s why before we save the new title and description, we’ll have to decode the title and description back to a normal text format using urldecode() function.

Now if we save all of our scripts and run the app, we should then be able to update the collections without any problem. Awesome!

Updating shopify product collection in bootstrap modal

Conclusion

Updating a product’s collection can be a hassle especially if the products are attached to not just a custom collection, but also to a smart collection. However, with collect API, we should be able to update the product’s collection without doing too much unnecessary API calls.

We hope that you learned how to update your products using collect API. If you have encountered any issues or errors, let us know in the comments below and we’ll try to help out! 🙂

Videoder Premium Downloader APK Latest Version 14.5 (Updated)

Videoder Premium APK for android is one of the best downloaders for downloading youtube videos in 2020. The free version of Videoder APP will only let you download videos but with Videoder premium, you will be able to download videos with no ads.

If you have been using YouTube downloaders for a long time then you may probably know already that Videoder is not available in the PlayStore. This is due to its violation of the PlayStore policies. So if you want to use this app, it is highly recommended to download directly from their official site.

What is Videoder?

Videoder is a free video downloader and converter for both android devices and PC.

Videoder was officially founded by Rahul Verma back in 2013 with the support of his team Varun, a full stack developer who is involved in both backend and frontend development of Videoder. Surbhi Singla is also part of Rahul’s team. She manages all of the operations involved in Videoder including brand promotion and marketing.

Up until now, Videoder is believed to be one of the most known video downloaders online.

Download Videos Everywhere

Videoder Premium APK supports over 50+ websites that stream videos and music including most of the popular sites like:

  • YouTube
  • Facebook
  • Instagram
  • Dailymotion
  • Twitter
  • Hotstar
  • voot
  • vidme
  • 9Anime
  • Soundcloud
  • 9gag
  • AudioBoom
  • IMDB
  • Funny Or Die
  • Liveleak
  • TED
  • Vine
  • Vimeo
  • VK
  • SonyLiv
  • Viu
  • RuTube
  • Youku
  • TVF Play
  • TikTok
  • Ozee
  • and more!

If one of your favorite streaming websites is not available in the list, you may contact Videoder and they will add them for just you!

New Features

Videoder Premium APK offers the following new features:

YouTube Playlist Downloader
YouTube Playlist Downloader

Videoder Premium APK allows you to download videos all at once with its YouTube Playlist Downloader feature

Download YouTube videos
Download YouTube videos

Download youtube videos to your phone or pc with Videoder for free.

Built-in Browser & Ad-Blocker
Built-in Browser & Ad-Blocker

Download videos on the go with built-in browser and with no interruptions thanks to Ad-block feature.

Insanely Fast Download
Insanely Fast Download

Videoder uses multiple network connections to boost your downloading speed.

Download Videoder Premium APK for Android

Videoder Premium APK is currently supporting Android devices, Windows PC and MAC. You may also download videos from YouTube Red using Videoder Premium.

To start downloading videos online, click the download button below.

How To Install Videoder Premium APK?

Step 1

Download Videoder Premium APK. If your browser warns you on downloading APK outside PlayStore, ignore it as you are downloading from the official Videoder site.

Videoder Download for Android and PC

Step 2

Wait for the app to be downloaded in the notifications panel. Once the download is complete, tap on the completed notification to install.

Tap to install Videoder

Step 3

Your settings may not allow you to install the APK outside Play Store. If you see the popup as shown below, tap on SETTINGS and follow the next instructions.

Go to your settings menu

Step 4

Go to ‘ Settings -> Security -> Unknown Sources ‘ and turn it on. You may turn this off again if you’ve finished installing videoder.

Switch on the unknown sources

Step 5

Go back to Downloads and open the downloaded and complete the installation. Do not forget to turn off Unknown Sources once the installation is complete.

Youtube downloader Videoder successfully installed

Conclusion

There’s no doubt that Videoder is one of the most used downloaders and converter online. But if for some reason the app doesn’t allow you to download then there are Videoder alternatives that you can use.

If you have encountered any issues. Don’t hesitate to let us know in the comments below. We’d love to help out!

Disclosure: This article may contain affiliate links, which means we may receive a commission if you click a link and purchase something that we have recommended.